blob: 2cc1fe4c514a2e84c52be79174d8bf6d8fb04a05 [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 Ossmanb03512c2018-06-20 16:03:23 +020083 supportsLocalCursor = true;
84 supportsDesktopResize = true;
85 supportsLEDState = true;
Pierre Ossman2fa63f82016-12-05 15:26:21 +010086
Pierre Ossmana22459d2014-03-17 14:42:10 +010087 if (customCompressLevel)
Pierre Ossmanb03512c2018-06-20 16:03:23 +020088 setCompressLevel(::compressLevel);
Pierre Ossman5156d5e2011-03-09 09:42:34 +000089
Pierre Ossmana22459d2014-03-17 14:42:10 +010090 if (!noJpeg)
Pierre Ossmanb03512c2018-06-20 16:03:23 +020091 setQualityLevel(::qualityLevel);
Pierre Ossman5156d5e2011-03-09 09:42:34 +000092
Pierre Ossman2a7a8d62013-02-15 08:33:39 +000093 if(sock == NULL) {
94 try {
Pierre Ossman5d055462018-05-03 14:04:38 +020095#ifndef WIN32
96 if (strchr(vncServerName, '/') != NULL) {
97 sock = new network::UnixSocket(vncServerName);
98 serverHost = sock->getPeerAddress();
Pierre Ossman4af93a92018-10-25 10:32:38 +020099 vlog.info(_("Connected to socket %s"), serverHost);
Pierre Ossman5d055462018-05-03 14:04:38 +0200100 } else
101#endif
102 {
103 getHostAndPort(vncServerName, &serverHost, &serverPort);
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000104
Pierre Ossman5d055462018-05-03 14:04:38 +0200105 sock = new network::TcpSocket(serverHost, serverPort);
Pierre Ossman4af93a92018-10-25 10:32:38 +0200106 vlog.info(_("Connected to host %s port %d"), serverHost, serverPort);
Pierre Ossman5d055462018-05-03 14:04:38 +0200107 }
Pierre Ossman2a7a8d62013-02-15 08:33:39 +0000108 } catch (rdr::Exception& e) {
109 vlog.error("%s", e.str());
Dr. David Alan Gilbertf4d1d892017-07-11 12:11:50 +0100110 if (alertOnFatalError)
111 fl_alert("%s", e.str());
Pierre Ossman2a7a8d62013-02-15 08:33:39 +0000112 exit_vncviewer();
113 return;
114 }
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000115 }
116
117 Fl::add_fd(sock->getFd(), FL_READ | FL_EXCEPT, socketEvent, this);
118
119 // See callback below
120 sock->inStream().setBlockCallback(this);
121
122 setServerName(serverHost);
123 setStreams(&sock->inStream(), &sock->outStream());
124
125 initialiseProtocol();
Pierre Ossmanf4f30942011-05-17 09:39:07 +0000126
127 OptionsDialog::addCallback(handleOptions, this);
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000128}
129
130CConn::~CConn()
131{
Pierre Ossmanf4f30942011-05-17 09:39:07 +0000132 OptionsDialog::removeCallback(handleOptions);
Pierre Ossmand9b90032015-09-23 12:18:52 +0200133 Fl::remove_timeout(handleUpdateTimeout, this);
Pierre Ossmanf4f30942011-05-17 09:39:07 +0000134
Pierre Ossman6a9e2e62011-05-19 14:47:43 +0000135 if (desktop)
136 delete desktop;
137
Pierre Ossman5e04c262011-11-09 11:31:12 +0000138 delete [] serverHost;
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000139 if (sock)
140 Fl::remove_fd(sock->getFd());
141 delete sock;
142}
143
Pierre Ossman2eb1d112011-05-16 12:18:08 +0000144const char *CConn::connectionInfo()
145{
146 static char infoText[1024] = "";
147
Pierre Ossmanb2046432014-09-22 11:17:34 +0200148 char scratch[100];
Pierre Ossman2eb1d112011-05-16 12:18:08 +0000149 char pfStr[100];
Pierre Ossmanb2046432014-09-22 11:17:34 +0200150
151 // Crude way of avoiding constant overflow checks
152 assert((sizeof(scratch) + 1) * 10 < sizeof(infoText));
153
154 infoText[0] = '\0';
155
156 snprintf(scratch, sizeof(scratch),
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200157 _("Desktop name: %.80s"), server.name());
Pierre Ossmanb2046432014-09-22 11:17:34 +0200158 strcat(infoText, scratch);
159 strcat(infoText, "\n");
160
161 snprintf(scratch, sizeof(scratch),
162 _("Host: %.80s port: %d"), serverHost, serverPort);
163 strcat(infoText, scratch);
164 strcat(infoText, "\n");
165
166 snprintf(scratch, sizeof(scratch),
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200167 _("Size: %d x %d"), server.width(), server.height());
Pierre Ossmanb2046432014-09-22 11:17:34 +0200168 strcat(infoText, scratch);
169 strcat(infoText, "\n");
Pierre Ossman2eb1d112011-05-16 12:18:08 +0000170
Pierre Ossman744e55c2014-12-03 14:00:54 +0100171 // TRANSLATORS: Will be filled in with a string describing the
172 // protocol pixel format in a fairly language neutral way
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200173 server.pf().print(pfStr, 100);
Pierre Ossmanb2046432014-09-22 11:17:34 +0200174 snprintf(scratch, sizeof(scratch),
175 _("Pixel format: %s"), pfStr);
176 strcat(infoText, scratch);
177 strcat(infoText, "\n");
Pierre Ossman2eb1d112011-05-16 12:18:08 +0000178
Pierre Ossman744e55c2014-12-03 14:00:54 +0100179 // TRANSLATORS: Similar to the earlier "Pixel format" string
Pierre Ossmanb2046432014-09-22 11:17:34 +0200180 serverPF.print(pfStr, 100);
181 snprintf(scratch, sizeof(scratch),
182 _("(server default %s)"), pfStr);
183 strcat(infoText, scratch);
184 strcat(infoText, "\n");
Pierre Ossman2eb1d112011-05-16 12:18:08 +0000185
Pierre Ossmanb2046432014-09-22 11:17:34 +0200186 snprintf(scratch, sizeof(scratch),
Pierre Ossmanef6881b2018-06-20 11:26:18 +0200187 _("Requested encoding: %s"), encodingName(getPreferredEncoding()));
Pierre Ossmanb2046432014-09-22 11:17:34 +0200188 strcat(infoText, scratch);
189 strcat(infoText, "\n");
190
191 snprintf(scratch, sizeof(scratch),
192 _("Last used encoding: %s"), encodingName(lastServerEncoding));
193 strcat(infoText, scratch);
194 strcat(infoText, "\n");
195
196 snprintf(scratch, sizeof(scratch),
197 _("Line speed estimate: %d kbit/s"), sock->inStream().kbitsPerSecond());
198 strcat(infoText, scratch);
199 strcat(infoText, "\n");
200
201 snprintf(scratch, sizeof(scratch),
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200202 _("Protocol version: %d.%d"), server.majorVersion, server.minorVersion);
Pierre Ossmanb2046432014-09-22 11:17:34 +0200203 strcat(infoText, scratch);
204 strcat(infoText, "\n");
205
206 snprintf(scratch, sizeof(scratch),
207 _("Security method: %s"), secTypeName(csecurity->getType()));
208 strcat(infoText, scratch);
209 strcat(infoText, "\n");
Pierre Ossman2eb1d112011-05-16 12:18:08 +0000210
211 return infoText;
212}
213
Pierre Ossmanfecf0a42018-03-26 12:22:47 +0200214unsigned CConn::getUpdateCount()
Pierre Ossman921f6c82017-02-24 12:33:09 +0100215{
Pierre Ossmanfecf0a42018-03-26 12:22:47 +0200216 return updateCount;
Pierre Ossman921f6c82017-02-24 12:33:09 +0100217}
218
219unsigned CConn::getPixelCount()
220{
221 return pixelCount;
222}
223
224unsigned CConn::getPosition()
225{
226 return sock->inStream().pos();
227}
228
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000229// The RFB core is not properly asynchronous, so it calls this callback
230// whenever it needs to block to wait for more data. Since FLTK is
231// monitoring the socket, we just make sure FLTK gets to run.
232
233void CConn::blockCallback()
234{
Pierre Ossman5102fa92015-11-18 16:23:21 +0100235 run_mainloop();
Pierre Ossman1db73242015-09-23 12:20:32 +0200236
237 if (should_exit())
238 throw rdr::Exception("Termination requested");
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000239}
240
DRC3e7ed812013-02-26 10:34:22 +0000241void CConn::socketEvent(FL_SOCKET fd, void *data)
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000242{
243 CConn *cc;
244 static bool recursing = false;
245
246 assert(data);
247 cc = (CConn*)data;
248
249 // I don't think processMsg() is recursion safe, so add this check
250 if (recursing)
251 return;
252
253 recursing = true;
254
255 try {
256 // processMsg() only processes one message, so we need to loop
257 // until the buffers are empty or things will stall.
258 do {
259 cc->processMsg();
Pierre Ossman9a73adf2016-12-29 16:52:56 +0100260
261 // Make sure that the FLTK handling and the timers gets some CPU
262 // time in case of back to back messages
263 Fl::check();
264 Timer::checkTimeouts();
265
266 // Also check if we need to stop reading and terminate
267 if (should_exit())
268 break;
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000269 } while (cc->sock->inStream().checkNoWait(1));
270 } catch (rdr::EndOfStream& e) {
Pierre Ossmanad8609a2012-04-26 09:04:14 +0000271 vlog.info("%s", e.str());
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000272 exit_vncviewer();
273 } catch (rdr::Exception& e) {
Pierre Ossmanad8609a2012-04-26 09:04:14 +0000274 vlog.error("%s", e.str());
Pierre Ossman1db73242015-09-23 12:20:32 +0200275 // Somebody might already have requested us to terminate, and
276 // might have already provided an error message.
277 if (!should_exit())
278 exit_vncviewer(e.str());
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000279 }
280
281 recursing = false;
282}
283
284////////////////////// CConnection callback methods //////////////////////
285
Pierre Ossman2affd772018-06-20 07:03:10 +0200286// initDone() is called when the serverInit message has been received. At
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000287// this point we create the desktop window and display it. We also tell the
288// server the pixel format and encodings to use and request the first update.
Pierre Ossman2affd772018-06-20 07:03:10 +0200289void CConn::initDone()
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000290{
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000291 // If using AutoSelect with old servers, start in FullColor
292 // mode. See comment in autoSelectFormatAndEncoding.
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200293 if (server.beforeVersion(3, 8) && autoSelect)
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000294 fullColour.setParam(true);
295
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200296 serverPF = server.pf();
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000297
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200298 desktop = new DesktopWindow(server.width(), server.height(),
299 server.name(), serverPF, this);
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000300 fullColourPF = desktop->getPreferredPF();
301
Pierre Ossman5d512c32011-11-04 11:42:16 +0000302 // Force a switch to the format and encoding we'd like
Pierre Ossmanef6881b2018-06-20 11:26:18 +0200303 updatePixelFormat();
304 int encNum = encodingNum(::preferredEncoding);
305 if (encNum != -1)
306 setPreferredEncoding(encNum);
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000307}
308
309// setDesktopSize() is called when the desktop size changes (including when
310// it is set initially).
311void CConn::setDesktopSize(int w, int h)
312{
313 CConnection::setDesktopSize(w,h);
314 resizeFramebuffer();
315}
316
317// setExtendedDesktopSize() is a more advanced version of setDesktopSize()
Pierre Ossman28c1d542015-03-03 16:27:44 +0100318void CConn::setExtendedDesktopSize(unsigned reason, unsigned result,
319 int w, int h, const rfb::ScreenSet& layout)
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000320{
321 CConnection::setExtendedDesktopSize(reason, result, w, h, layout);
322
323 if ((reason == reasonClient) && (result != resultSuccess)) {
324 vlog.error(_("SetDesktopSize failed: %d"), result);
325 return;
326 }
327
328 resizeFramebuffer();
329}
330
331// setName() is called when the desktop name changes
332void CConn::setName(const char* name)
333{
334 CConnection::setName(name);
Pierre Ossmandd45b442018-10-31 17:08:59 +0100335 desktop->setName(name);
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000336}
337
338// framebufferUpdateStart() is called at the beginning of an update.
339// Here we try to send out a new framebuffer update request so that the
340// next update can be sent out in parallel with us decoding the current
Pierre Ossman5d512c32011-11-04 11:42:16 +0000341// one.
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000342void CConn::framebufferUpdateStart()
343{
Pierre Ossman3da238d2015-11-12 12:20:05 +0100344 CConnection::framebufferUpdateStart();
345
Pierre Ossmand9b90032015-09-23 12:18:52 +0200346 // Update the screen prematurely for very slow updates
347 Fl::add_timeout(1.0, handleUpdateTimeout, this);
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000348}
349
350// framebufferUpdateEnd() is called at the end of an update.
351// For each rectangle, the FdInStream will have timed the speed
352// of the connection, allowing us to select format and encoding
353// appropriately, and then request another incremental update.
354void CConn::framebufferUpdateEnd()
355{
Pierre Ossman3da238d2015-11-12 12:20:05 +0100356 CConnection::framebufferUpdateEnd();
357
Pierre Ossmanfecf0a42018-03-26 12:22:47 +0200358 updateCount++;
Pierre Ossman921f6c82017-02-24 12:33:09 +0100359
Pierre Ossmand9b90032015-09-23 12:18:52 +0200360 Fl::remove_timeout(handleUpdateTimeout, this);
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000361 desktop->updateWindow();
362
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000363 // Compute new settings based on updated bandwidth values
364 if (autoSelect)
365 autoSelectFormatAndEncoding();
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000366}
367
368// The rest of the callbacks are fairly self-explanatory...
369
370void CConn::setColourMapEntries(int firstColour, int nColours, rdr::U16* rgbs)
371{
Pierre Ossman8ca4c1d2014-09-22 12:54:26 +0200372 vlog.error(_("Invalid SetColourMapEntries from server!"));
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000373}
374
375void CConn::bell()
376{
377 fl_beep();
378}
379
Pierre Ossman66f1db52019-05-02 12:32:03 +0200380void CConn::serverCutText(const char* str)
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000381{
Pierre Ossman66f1db52019-05-02 12:32:03 +0200382 desktop->serverCutText(str);
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000383}
384
Pierre Ossmanfdba3fe2014-01-31 13:12:18 +0100385void CConn::dataRect(const Rect& r, int encoding)
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000386{
387 sock->inStream().startTiming();
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000388
Pierre Ossmanfdba3fe2014-01-31 13:12:18 +0100389 if (encoding != encodingCopyRect)
390 lastServerEncoding = encoding;
391
Pierre Ossman9f273e92015-11-09 16:34:54 +0100392 CConnection::dataRect(r, encoding);
Pierre Ossmanfdba3fe2014-01-31 13:12:18 +0100393
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000394 sock->inStream().stopTiming();
Pierre Ossman921f6c82017-02-24 12:33:09 +0100395
396 pixelCount += r.area();
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000397}
398
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000399void CConn::setCursor(int width, int height, const Point& hotspot,
Pierre Ossman6a1a0d02017-02-19 15:48:17 +0100400 const rdr::U8* data)
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000401{
Pierre Ossman6a1a0d02017-02-19 15:48:17 +0100402 desktop->setCursor(width, height, hotspot, data);
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000403}
404
Pierre Ossmane28bdb22011-11-14 16:02:06 +0000405void CConn::fence(rdr::U32 flags, unsigned len, const char data[])
406{
407 CMsgHandler::fence(flags, len, data);
408
409 if (flags & fenceFlagRequest) {
410 // We handle everything synchronously so we trivially honor these modes
411 flags = flags & (fenceFlagBlockBefore | fenceFlagBlockAfter);
412
413 writer()->writeFence(flags, len, data);
414 return;
415 }
Pierre Ossman6eecbca2018-06-19 10:42:24 +0200416}
Pierre Ossmane28bdb22011-11-14 16:02:06 +0000417
Pierre Ossman2fa63f82016-12-05 15:26:21 +0100418void CConn::setLEDState(unsigned int state)
419{
420 CConnection::setLEDState(state);
421
422 desktop->setLEDState(state);
423}
424
DRC33c15e32011-11-03 18:49:21 +0000425
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000426////////////////////// Internal methods //////////////////////
427
428void CConn::resizeFramebuffer()
429{
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200430 desktop->resizeFramebuffer(server.width(), server.height());
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000431}
432
433// autoSelectFormatAndEncoding() chooses the format and encoding appropriate
434// to the connection speed:
435//
436// First we wait for at least one second of bandwidth measurement.
437//
438// Above 16Mbps (i.e. LAN), we choose the second highest JPEG quality,
439// which should be perceptually lossless.
440//
441// If the bandwidth is below that, we choose a more lossy JPEG quality.
442//
443// If the bandwidth drops below 256 Kbps, we switch to palette mode.
444//
445// Note: The system here is fairly arbitrary and should be replaced
446// with something more intelligent at the server end.
447//
448void CConn::autoSelectFormatAndEncoding()
449{
450 int kbitsPerSecond = sock->inStream().kbitsPerSecond();
451 unsigned int timeWaited = sock->inStream().timeWaited();
452 bool newFullColour = fullColour;
Pierre Ossmanb03512c2018-06-20 16:03:23 +0200453 int newQualityLevel = ::qualityLevel;
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000454
455 // Always use Tight
Pierre Ossmanef6881b2018-06-20 11:26:18 +0200456 setPreferredEncoding(encodingTight);
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000457
458 // Check that we have a decent bandwidth measurement
459 if ((kbitsPerSecond == 0) || (timeWaited < 10000))
460 return;
461
462 // Select appropriate quality level
463 if (!noJpeg) {
464 if (kbitsPerSecond > 16000)
465 newQualityLevel = 8;
466 else
467 newQualityLevel = 6;
468
Pierre Ossmanb03512c2018-06-20 16:03:23 +0200469 if (newQualityLevel != ::qualityLevel) {
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000470 vlog.info(_("Throughput %d kbit/s - changing to quality %d"),
471 kbitsPerSecond, newQualityLevel);
Pierre Ossmanb03512c2018-06-20 16:03:23 +0200472 ::qualityLevel.setParam(newQualityLevel);
Pierre Ossmanef6881b2018-06-20 11:26:18 +0200473 setQualityLevel(newQualityLevel);
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000474 }
475 }
476
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200477 if (server.beforeVersion(3, 8)) {
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000478 // Xvnc from TightVNC 1.2.9 sends out FramebufferUpdates with
479 // cursors "asynchronously". If this happens in the middle of a
480 // pixel format change, the server will encode the cursor with
481 // the old format, but the client will try to decode it
482 // according to the new format. This will lead to a
483 // crash. Therefore, we do not allow automatic format change for
484 // old servers.
485 return;
486 }
487
488 // Select best color level
489 newFullColour = (kbitsPerSecond > 256);
490 if (newFullColour != fullColour) {
Pierre Ossman4af93a92018-10-25 10:32:38 +0200491 if (newFullColour)
492 vlog.info(_("Throughput %d kbit/s - full color is now enabled"),
493 kbitsPerSecond);
494 else
495 vlog.info(_("Throughput %d kbit/s - full color is now disabled"),
496 kbitsPerSecond);
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000497 fullColour.setParam(newFullColour);
Pierre Ossmanef6881b2018-06-20 11:26:18 +0200498 updatePixelFormat();
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000499 }
500}
501
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000502// requestNewUpdate() requests an update from the server, having set the
503// format and encoding appropriately.
Pierre Ossmanef6881b2018-06-20 11:26:18 +0200504void CConn::updatePixelFormat()
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000505{
Pierre Ossmanef6881b2018-06-20 11:26:18 +0200506 PixelFormat pf;
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000507
Pierre Ossmanef6881b2018-06-20 11:26:18 +0200508 if (fullColour) {
509 pf = fullColourPF;
510 } else {
511 if (lowColourLevel == 0)
512 pf = verylowColourPF;
513 else if (lowColourLevel == 1)
514 pf = lowColourPF;
515 else
516 pf = mediumColourPF;
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000517 }
Pierre Ossmand4c61ce2011-04-29 11:18:12 +0000518
Pierre Ossmanef6881b2018-06-20 11:26:18 +0200519 char str[256];
520 pf.print(str, 256);
521 vlog.info(_("Using pixel format %s"),str);
522 setPF(pf);
Pierre Ossman5156d5e2011-03-09 09:42:34 +0000523}
Pierre Ossmanf4f30942011-05-17 09:39:07 +0000524
525void CConn::handleOptions(void *data)
526{
527 CConn *self = (CConn*)data;
528
529 // Checking all the details of the current set of encodings is just
530 // a pain. Assume something has changed, as resending the encoding
531 // list is cheap. Avoid overriding what the auto logic has selected
532 // though.
533 if (!autoSelect) {
Pierre Ossmanef6881b2018-06-20 11:26:18 +0200534 int encNum = encodingNum(::preferredEncoding);
Pierre Ossmanf4f30942011-05-17 09:39:07 +0000535
536 if (encNum != -1)
Pierre Ossmanef6881b2018-06-20 11:26:18 +0200537 self->setPreferredEncoding(encNum);
Pierre Ossmanf4f30942011-05-17 09:39:07 +0000538 }
539
Pierre Ossmana22459d2014-03-17 14:42:10 +0100540 if (customCompressLevel)
Pierre Ossmanb03512c2018-06-20 16:03:23 +0200541 self->setCompressLevel(::compressLevel);
Pierre Ossmana22459d2014-03-17 14:42:10 +0100542 else
Pierre Ossmanef6881b2018-06-20 11:26:18 +0200543 self->setCompressLevel(-1);
Pierre Ossmanf4f30942011-05-17 09:39:07 +0000544
Pierre Ossmana22459d2014-03-17 14:42:10 +0100545 if (!noJpeg && !autoSelect)
Pierre Ossmanb03512c2018-06-20 16:03:23 +0200546 self->setQualityLevel(::qualityLevel);
Pierre Ossmana22459d2014-03-17 14:42:10 +0100547 else
Pierre Ossmanef6881b2018-06-20 11:26:18 +0200548 self->setQualityLevel(-1);
Pierre Ossmanf4f30942011-05-17 09:39:07 +0000549
Pierre Ossmanef6881b2018-06-20 11:26:18 +0200550 self->updatePixelFormat();
Pierre Ossmanf4f30942011-05-17 09:39:07 +0000551}
Pierre Ossmand9b90032015-09-23 12:18:52 +0200552
553void CConn::handleUpdateTimeout(void *data)
554{
555 CConn *self = (CConn *)data;
556
557 assert(self);
558
559 self->desktop->updateWindow();
560
561 Fl::repeat_timeout(1.0, handleUpdateTimeout, data);
562}