Pool Video Switch v2
Software video switch for distributed remote display in a lecture environment
vncthread.cpp
Go to the documentation of this file.
1 /*
2  # Copyright (c) 2009, 2010 - OpenSLX Project, Computer Center University of
3  # Freiburg
4  #
5  # This program is free software distributed under the GPL version 2.
6  # See http://openslx.org/COPYING
7  #
8  # If you have any feedback please consult http://openslx.org/feedback and
9  # send your suggestions, praise, or complaints to [email protected]
10  #
11  # General information about OpenSLX can be found at http://openslx.org/
12  # -----------------------------------------------------------------------------
13  # vncClientThread.cpp
14  # - Connection to remove vnc server
15  # - Emits Qt signal on framebuffer updates
16  # -----------------------------------------------------------------------------
17  */
18 
19 #include "vncthread.h"
20 #include "../../shared/util.h"
21 
22 #include <QPainter>
23 #include <utility>
24 #include <netinet/tcp.h>
25 
34 VncThread::VncThread(QString host, int port, QString passwd, int quality)
35  : QThread()
36  , _host(std::move(host))
37  , _port(port)
38  , _passwd(std::move(passwd))
39  , _quality(quality)
40  , _run(true)
41  , _started(false)
42 {
43  moveToThread(this);
44 }
45 
47 {
48  qDebug("VNC worker destructor called.");
49  Q_ASSERT(_run == false);
50  if (_client != nullptr) {
51  if (_client->sock != -1)
52  ::close(_client->sock);
53  _client->sock = -1;
54  rfbClientCleanup(_client);
55  }
56 }
57 
59 // Public
60 
72 {
73  qDebug("[%s] VNC client started.", metaObject()->className());
74  qDebug("[%s] Host: '%s' Port: %i Passwd: '%s' Quality: %i", metaObject()->className(), qPrintable(_host), _port,
75  qPrintable(_passwd), _quality);
76 
77  // setup network
78  for (int retry = 0; retry < 5 && _run; ++retry) {
79  msleep(1 + slxrand() % 60);
80  if (!_run)
81  break;
82  _client = rfbGetClient(8, 3, 4);
83  _client->MallocFrameBuffer = &frameBufferHandler;
84  _client->canHandleNewFBSize = true;
85  free(_client->serverHost); // in rfbGetClient, serverHost is assigned strdup(""), so free that first.
86  _client->serverHost = strdup(_host.toUtf8().constData());
87  _client->desktopName = nullptr;
88  _client->serverPort = _port;
89  _client->GetPassword = &passwdHandler;
90  _client->GotFrameBufferUpdate = &updateImage;
91  _client->frameBuffer = nullptr;
92 
93  // save this instance in vnc-struct for callbacks
94  rfbClientSetClientData(_client, nullptr, this);
95 
96  // start client
97  if (rfbInitClient(_client, nullptr, nullptr)) {
98  break; // Success!
99  }
100  // Connection failed
101  _client = nullptr; // InitClient frees the client on failure, so make sure we don't keep an invalid pointer around
102  if (!_run)
103  break;
104  // error, let's try again
105  msleep(10 + slxrand() % 50);
106  }
107  if (_client != nullptr) {
108  qDebug("[%s] Connection successful!", metaObject()->className());
109  int one = 1;
110  setsockopt(_client->sock, SOL_TCP, TCP_NODELAY, &one, sizeof(one));
111  one = 1;
112  setsockopt(_client->sock, SOL_TCP, TCP_QUICKACK, &one, sizeof(one));
113 
114  // Main VNC event loop
115  if (_run) {
116  _connected = true;
117  }
118  while (_run) {
119  const int i = WaitForMessage(_client, 100 * 1000); // wait 100ms for message. returns -1 on error/disconnect, 0 if nothing happened, 1 if new data arrived
120  if (i < 0)
121  break;
122  if (i > 0 && !HandleRFBServerMessage(_client))
123  break;
124  }
125  }
126 
127  _connected = false;
128  emit projectionStopped();
129  while (_run)
130  msleep(100);
131  qDebug("[%s] VNC client stopped.", metaObject()->className());
132 }
133 
140 {
141  if (_client == nullptr || _client->desktopName == nullptr)
142  return {};
143  return QString(_client->desktopName);
144 }
145 
156 void VncThread::processImageUpdate(int x, int y, int w, int h)
157 {
158  emit imageUpdated(x, y, w, h);
159 }
160 
162 {
163  emit projectionStarted();
164 }
165 
166 // *** callback stuff ***
167 
175 char* VncThread::passwdHandler(rfbClient *client)
176 {
177  auto* t = reinterpret_cast<VncThread*>(rfbClientGetClientData(client, nullptr));
178  return strdup(t->_passwd.toUtf8());
179 }
180 
188 rfbBool VncThread::frameBufferHandler(rfbClient *client)
189 {
190  auto *t = reinterpret_cast<VncThread*>(rfbClientGetClientData(client, nullptr));
191  const int width = client->width, height = client->height, depth = 32;
192  const int size = width * height * (depth / 8);
193  qDebug("[%s] Remote desktop: %ix%ix%i", t->metaObject()->className(), width, height, depth);
194  if (width < 0 || height < 0 || size < 0) {
195  qWarning() << "IGNORING INVALID FRAMEBUFFER SIZE";
196  client->frameBuffer = nullptr;
197  return false;
198  }
199 
200  t->_img = QSharedPointer<QImage>(new QImage(width, height, QImage::Format_RGB32));
201  if (size > t->_img->sizeInBytes()) {
202  qDebug() << "Fatal: Created image too small:" << t->_img->sizeInBytes() << "<" << size;
203  ::exit(1);
204  }
205  client->frameBuffer = t->_img->bits();
206  memset(client->frameBuffer, '\0', size_t(size));
207  client->format.trueColour = 1;
208  client->format.bitsPerPixel = depth;
209  client->format.redShift = 16;
210  client->format.greenShift = 8;
211  client->format.blueShift = 0;
212  client->format.redMax = 0xff;
213  client->format.greenMax = 0xff;
214  client->format.blueMax = 0xff;
215 
216  const int quality = t->_quality;
217  switch (quality) {
218  case VncThread::HIGH:
219  client->appData.useBGR233 = 0;
220  client->appData.encodingsString = "copyrect zlib hextile raw";
221  client->appData.compressLevel = 4;
222  client->appData.qualityLevel = 9;
223  client->appData.scaleSetting = 0;
224  break;
225  case VncThread::MEDIUM:
226  client->appData.useBGR233 = 0;
227  client->appData.encodingsString = "copyrect tight zrle ultra zlib hextile corre rre raw";
228  client->appData.compressLevel = 7;
229  client->appData.qualityLevel = 8;
230  client->appData.scaleSetting = 0;
231  break;
232  case VncThread::LOW:
233  default:
234  client->appData.useBGR233 = 1;
235  client->appData.encodingsString = "copyrect tight zrle ultra zlib hextile corre rre raw";
236  client->appData.compressLevel = 9;
237  client->appData.qualityLevel = 1;
238  client->appData.scaleSetting = 0;
239  break;
240  }
241  SetFormatAndEncodings(client);
242 
243  SendFramebufferUpdateRequest(client, 0, 0, width, height, false);
244 
245  if (!t->_started) {
246  t->_started = true;
247  t->emitStarted();
248  }
249 
250  return true;
251 }
252 
263 void VncThread::updateImage(rfbClient* client, int x, int y, int w, int h)
264 {
265  auto* t = (VncThread*)rfbClientGetClientData(client, 0);
266  t->processImageUpdate(x, y, w, h);
267 }
QString _passwd
Definition: vncthread.h:45
VncThread(QString host, int port, QString passwd, int quality)
Initialize this VNC client connection worker thread.
Definition: vncthread.cpp:34
void run() override
Worker thread's mainloop, connecting to the VNC server and handling the connection.
Definition: vncthread.cpp:71
void projectionStopped()
int static const MEDIUM
Definition: vncthread.h:75
void emitStarted()
Definition: vncthread.cpp:161
~VncThread() override
Definition: vncthread.cpp:46
QString getDesktopName() const
Get name of the VNC server's desktop.
Definition: vncthread.cpp:139
void projectionStarted()
QString _host
Definition: vncthread.h:43
int _quality
Definition: vncthread.h:46
void processImageUpdate(int x, int y, int w, int h)
Handle update of an area of the VNC framebuffer.
Definition: vncthread.cpp:156
VncThread - communicate with VNC server, scale image if necessary.
Definition: vncthread.h:36
static char * passwdHandler(rfbClient *client)
Callback for the vnc client lib: The VNC server is requesting a password.
Definition: vncthread.cpp:175
volatile bool _run
Definition: vncthread.h:51
void imageUpdated(const int x, const int y, const int w, const int h)
#define slxrand()
Definition: util.h:19
int static const HIGH
Definition: vncthread.h:74
rfbClient * _client
Definition: vncthread.h:41
volatile bool _connected
Definition: vncthread.h:50
int _port
Definition: vncthread.h:44
int static const LOW
Definition: vncthread.h:76
QSharedPointer< QImage > _img
Definition: vncthread.h:48
static rfbBool frameBufferHandler(rfbClient *client)
Callback for the vnc client lib: The size of the remote screen has been changed, so we're supposed to...
Definition: vncthread.cpp:188
static void updateImage(rfbClient *client, int x, int y, int w, int h)
Callback for the vnc client lib: A part of the frame buffer has changed.
Definition: vncthread.cpp:263