1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtGui module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39#include "qvnc_p.h"
40#include "qvncscreen.h"
41#include "qvncclient.h"
42#include "QtNetwork/qtcpserver.h"
43#include "QtNetwork/qtcpsocket.h"
44#include <qendian.h>
45#include <qthread.h>
46
47#include <QtGui/qguiapplication.h>
48#include <QtGui/QWindow>
49
50#ifdef Q_OS_WIN
51#include <Winsock2.h>
52#else
53#include <arpa/inet.h>
54#endif
55
56#include <QtCore/QDebug>
57
58QT_BEGIN_NAMESPACE
59
60Q_LOGGING_CATEGORY(lcVnc, "qt.qpa.vnc");
61
62QVncDirtyMap::QVncDirtyMap(QVncScreen *screen)
63 : screen(screen), bytesPerPixel(0), numDirty(0)
64{
65 bytesPerPixel = (screen->depth() + 7) / 8;
66 bufferWidth = screen->geometry().width();
67 bufferHeight = screen->geometry().height();
68 bufferStride = bufferWidth * bytesPerPixel;
69 buffer = new uchar[bufferHeight * bufferStride];
70
71 mapWidth = (bufferWidth + MAP_TILE_SIZE - 1) / MAP_TILE_SIZE;
72 mapHeight = (bufferHeight + MAP_TILE_SIZE - 1) / MAP_TILE_SIZE;
73 numTiles = mapWidth * mapHeight;
74 map = new uchar[numTiles];
75}
76
77QVncDirtyMap::~QVncDirtyMap()
78{
79 delete[] map;
80 delete[] buffer;
81}
82
83void QVncDirtyMap::reset()
84{
85 memset(s: map, c: 1, n: numTiles);
86 memset(s: buffer, c: 0, n: bufferHeight * bufferStride);
87 numDirty = numTiles;
88}
89
90inline bool QVncDirtyMap::dirty(int x, int y) const
91{
92 return map[y * mapWidth + x];
93}
94
95inline void QVncDirtyMap::setClean(int x, int y)
96{
97 map[y * mapWidth + x] = 0;
98 --numDirty;
99}
100
101template <class T>
102void QVncDirtyMapOptimized<T>::setDirty(int tileX, int tileY, bool force)
103{
104 static bool alwaysForce = qEnvironmentVariableIsSet(varName: "QT_VNC_NO_COMPAREBUFFER");
105 if (alwaysForce)
106 force = true;
107
108 bool changed = false;
109
110 if (!force) {
111 const int lstep = bufferStride;
112 const int startX = tileX * MAP_TILE_SIZE;
113 const int startY = tileY * MAP_TILE_SIZE;
114 const uchar *scrn = screen->image()->constBits()
115 + startY * lstep + startX * bytesPerPixel;
116 uchar *old = buffer + startY * bufferStride + startX * sizeof(T);
117
118 const int tileHeight = (startY + MAP_TILE_SIZE > bufferHeight ?
119 bufferHeight - startY : MAP_TILE_SIZE);
120 const int tileWidth = (startX + MAP_TILE_SIZE > bufferWidth ?
121 bufferWidth - startX : MAP_TILE_SIZE);
122 const bool doInlines = (tileWidth == MAP_TILE_SIZE);
123
124 int y = tileHeight;
125
126 if (doInlines) { // hw: memcmp/memcpy is inlined when using constants
127 while (y) {
128 if (memcmp(s1: old, s2: scrn, n: sizeof(T) * MAP_TILE_SIZE)) {
129 changed = true;
130 break;
131 }
132 scrn += lstep;
133 old += bufferStride;
134 --y;
135 }
136
137 while (y) {
138 memcpy(dest: old, src: scrn, n: sizeof(T) * MAP_TILE_SIZE);
139 scrn += lstep;
140 old += bufferStride;
141 --y;
142 }
143 } else {
144 while (y) {
145 if (memcmp(s1: old, s2: scrn, n: sizeof(T) * tileWidth)) {
146 changed = true;
147 break;
148 }
149 scrn += lstep;
150 old += bufferStride;
151 --y;
152 }
153
154 while (y) {
155 memcpy(dest: old, src: scrn, n: sizeof(T) * tileWidth);
156 scrn += lstep;
157 old += bufferStride;
158 --y;
159 }
160 }
161 }
162
163 const int mapIndex = tileY * mapWidth + tileX;
164 if ((force || changed) && !map[mapIndex]) {
165 map[mapIndex] = 1;
166 ++numDirty;
167 }
168}
169
170template class QVncDirtyMapOptimized<unsigned char>;
171template class QVncDirtyMapOptimized<unsigned short>;
172template class QVncDirtyMapOptimized<unsigned int>;
173
174static const struct {
175 int keysym;
176 int keycode;
177} keyMap[] = {
178 { .keysym: 0xff08, .keycode: Qt::Key_Backspace },
179 { .keysym: 0xff09, .keycode: Qt::Key_Tab },
180 { .keysym: 0xff0d, .keycode: Qt::Key_Return },
181 { .keysym: 0xff1b, .keycode: Qt::Key_Escape },
182 { .keysym: 0xff63, .keycode: Qt::Key_Insert },
183 { .keysym: 0xffff, .keycode: Qt::Key_Delete },
184 { .keysym: 0xff50, .keycode: Qt::Key_Home },
185 { .keysym: 0xff57, .keycode: Qt::Key_End },
186 { .keysym: 0xff55, .keycode: Qt::Key_PageUp },
187 { .keysym: 0xff56, .keycode: Qt::Key_PageDown },
188 { .keysym: 0xff51, .keycode: Qt::Key_Left },
189 { .keysym: 0xff52, .keycode: Qt::Key_Up },
190 { .keysym: 0xff53, .keycode: Qt::Key_Right },
191 { .keysym: 0xff54, .keycode: Qt::Key_Down },
192 { .keysym: 0xffbe, .keycode: Qt::Key_F1 },
193 { .keysym: 0xffbf, .keycode: Qt::Key_F2 },
194 { .keysym: 0xffc0, .keycode: Qt::Key_F3 },
195 { .keysym: 0xffc1, .keycode: Qt::Key_F4 },
196 { .keysym: 0xffc2, .keycode: Qt::Key_F5 },
197 { .keysym: 0xffc3, .keycode: Qt::Key_F6 },
198 { .keysym: 0xffc4, .keycode: Qt::Key_F7 },
199 { .keysym: 0xffc5, .keycode: Qt::Key_F8 },
200 { .keysym: 0xffc6, .keycode: Qt::Key_F9 },
201 { .keysym: 0xffc7, .keycode: Qt::Key_F10 },
202 { .keysym: 0xffc8, .keycode: Qt::Key_F11 },
203 { .keysym: 0xffc9, .keycode: Qt::Key_F12 },
204 { .keysym: 0xffe1, .keycode: Qt::Key_Shift },
205 { .keysym: 0xffe2, .keycode: Qt::Key_Shift },
206 { .keysym: 0xffe3, .keycode: Qt::Key_Control },
207 { .keysym: 0xffe4, .keycode: Qt::Key_Control },
208 { .keysym: 0xffe7, .keycode: Qt::Key_Meta },
209 { .keysym: 0xffe8, .keycode: Qt::Key_Meta },
210 { .keysym: 0xffe9, .keycode: Qt::Key_Alt },
211 { .keysym: 0xffea, .keycode: Qt::Key_Alt },
212
213 { .keysym: 0xffb0, .keycode: Qt::Key_0 },
214 { .keysym: 0xffb1, .keycode: Qt::Key_1 },
215 { .keysym: 0xffb2, .keycode: Qt::Key_2 },
216 { .keysym: 0xffb3, .keycode: Qt::Key_3 },
217 { .keysym: 0xffb4, .keycode: Qt::Key_4 },
218 { .keysym: 0xffb5, .keycode: Qt::Key_5 },
219 { .keysym: 0xffb6, .keycode: Qt::Key_6 },
220 { .keysym: 0xffb7, .keycode: Qt::Key_7 },
221 { .keysym: 0xffb8, .keycode: Qt::Key_8 },
222 { .keysym: 0xffb9, .keycode: Qt::Key_9 },
223
224 { .keysym: 0xff8d, .keycode: Qt::Key_Return },
225 { .keysym: 0xffaa, .keycode: Qt::Key_Asterisk },
226 { .keysym: 0xffab, .keycode: Qt::Key_Plus },
227 { .keysym: 0xffad, .keycode: Qt::Key_Minus },
228 { .keysym: 0xffae, .keycode: Qt::Key_Period },
229 { .keysym: 0xffaf, .keycode: Qt::Key_Slash },
230
231 { .keysym: 0xff95, .keycode: Qt::Key_Home },
232 { .keysym: 0xff96, .keycode: Qt::Key_Left },
233 { .keysym: 0xff97, .keycode: Qt::Key_Up },
234 { .keysym: 0xff98, .keycode: Qt::Key_Right },
235 { .keysym: 0xff99, .keycode: Qt::Key_Down },
236 { .keysym: 0xff9a, .keycode: Qt::Key_PageUp },
237 { .keysym: 0xff9b, .keycode: Qt::Key_PageDown },
238 { .keysym: 0xff9c, .keycode: Qt::Key_End },
239 { .keysym: 0xff9e, .keycode: Qt::Key_Insert },
240 { .keysym: 0xff9f, .keycode: Qt::Key_Delete },
241
242 { .keysym: 0, .keycode: 0 }
243};
244
245void QRfbRect::read(QTcpSocket *s)
246{
247 quint16 buf[4];
248 s->read(data: (char*)buf, maxlen: 8);
249 x = ntohs(netshort: buf[0]);
250 y = ntohs(netshort: buf[1]);
251 w = ntohs(netshort: buf[2]);
252 h = ntohs(netshort: buf[3]);
253}
254
255void QRfbRect::write(QTcpSocket *s) const
256{
257 quint16 buf[4];
258 buf[0] = htons(hostshort: x);
259 buf[1] = htons(hostshort: y);
260 buf[2] = htons(hostshort: w);
261 buf[3] = htons(hostshort: h);
262 s->write(data: (char*)buf, len: 8);
263}
264
265void QRfbPixelFormat::read(QTcpSocket *s)
266{
267 char buf[16];
268 s->read(data: buf, maxlen: 16);
269 bitsPerPixel = buf[0];
270 depth = buf[1];
271 bigEndian = buf[2];
272 trueColor = buf[3];
273
274 quint16 a = ntohs(netshort: *(quint16 *)(buf + 4));
275 redBits = 0;
276 while (a) { a >>= 1; redBits++; }
277
278 a = ntohs(netshort: *(quint16 *)(buf + 6));
279 greenBits = 0;
280 while (a) { a >>= 1; greenBits++; }
281
282 a = ntohs(netshort: *(quint16 *)(buf + 8));
283 blueBits = 0;
284 while (a) { a >>= 1; blueBits++; }
285
286 redShift = buf[10];
287 greenShift = buf[11];
288 blueShift = buf[12];
289}
290
291void QRfbPixelFormat::write(QTcpSocket *s)
292{
293 char buf[16];
294 buf[0] = bitsPerPixel;
295 buf[1] = depth;
296 buf[2] = bigEndian;
297 buf[3] = trueColor;
298
299 quint16 a = 0;
300 for (int i = 0; i < redBits; i++) a = (a << 1) | 1;
301 *(quint16 *)(buf + 4) = htons(hostshort: a);
302
303 a = 0;
304 for (int i = 0; i < greenBits; i++) a = (a << 1) | 1;
305 *(quint16 *)(buf + 6) = htons(hostshort: a);
306
307 a = 0;
308 for (int i = 0; i < blueBits; i++) a = (a << 1) | 1;
309 *(quint16 *)(buf + 8) = htons(hostshort: a);
310
311 buf[10] = redShift;
312 buf[11] = greenShift;
313 buf[12] = blueShift;
314 s->write(data: buf, len: 16);
315}
316
317
318void QRfbServerInit::setName(const char *n)
319{
320 delete[] name;
321 name = new char [strlen(s: n) + 1];
322 strcpy(dest: name, src: n);
323}
324
325void QRfbServerInit::read(QTcpSocket *s)
326{
327 s->read(data: (char *)&width, maxlen: 2);
328 width = ntohs(netshort: width);
329 s->read(data: (char *)&height, maxlen: 2);
330 height = ntohs(netshort: height);
331 format.read(s);
332
333 quint32 len;
334 s->read(data: (char *)&len, maxlen: 4);
335 len = ntohl(netlong: len);
336
337 name = new char [len + 1];
338 s->read(data: name, maxlen: len);
339 name[len] = '\0';
340}
341
342void QRfbServerInit::write(QTcpSocket *s)
343{
344 quint16 t = htons(hostshort: width);
345 s->write(data: (char *)&t, len: 2);
346 t = htons(hostshort: height);
347 s->write(data: (char *)&t, len: 2);
348 format.write(s);
349 quint32 len = strlen(s: name);
350 len = htonl(hostlong: len);
351 s->write(data: (char *)&len, len: 4);
352 s->write(data: name, len: strlen(s: name));
353}
354
355bool QRfbSetEncodings::read(QTcpSocket *s)
356{
357 if (s->bytesAvailable() < 3)
358 return false;
359
360 char tmp;
361 s->read(data: &tmp, maxlen: 1); // padding
362 s->read(data: (char *)&count, maxlen: 2);
363 count = ntohs(netshort: count);
364
365 return true;
366}
367
368bool QRfbFrameBufferUpdateRequest::read(QTcpSocket *s)
369{
370 if (s->bytesAvailable() < 9)
371 return false;
372
373 s->read(data: &incremental, maxlen: 1);
374 rect.read(s);
375
376 return true;
377}
378
379bool QRfbKeyEvent::read(QTcpSocket *s)
380{
381 if (s->bytesAvailable() < 7)
382 return false;
383
384 s->read(data: &down, maxlen: 1);
385 quint16 tmp;
386 s->read(data: (char *)&tmp, maxlen: 2); // padding
387
388 quint32 key;
389 s->read(data: (char *)&key, maxlen: 4);
390 key = ntohl(netlong: key);
391
392 unicode = 0;
393 keycode = 0;
394 int i = 0;
395 while (keyMap[i].keysym && !keycode) {
396 if (keyMap[i].keysym == (int)key)
397 keycode = keyMap[i].keycode;
398 i++;
399 }
400
401 if (keycode >= ' ' && keycode <= '~')
402 unicode = keycode;
403
404 if (!keycode) {
405 if (key <= 0xff) {
406 unicode = key;
407 if (key >= 'a' && key <= 'z')
408 keycode = Qt::Key_A + key - 'a';
409 else if (key >= ' ' && key <= '~')
410 keycode = Qt::Key_Space + key - ' ';
411 }
412 }
413
414 return true;
415}
416
417bool QRfbPointerEvent::read(QTcpSocket *s)
418{
419 if (s->bytesAvailable() < 5)
420 return false;
421
422 char buttonMask;
423 s->read(data: &buttonMask, maxlen: 1);
424 buttons = Qt::NoButton;
425 if (buttonMask & 1)
426 buttons |= Qt::LeftButton;
427 if (buttonMask & 2)
428 buttons |= Qt::MiddleButton;
429 if (buttonMask & 4)
430 buttons |= Qt::RightButton;
431
432 quint16 tmp;
433 s->read(data: (char *)&tmp, maxlen: 2);
434 x = ntohs(netshort: tmp);
435 s->read(data: (char *)&tmp, maxlen: 2);
436 y = ntohs(netshort: tmp);
437
438 return true;
439}
440
441bool QRfbClientCutText::read(QTcpSocket *s)
442{
443 if (s->bytesAvailable() < 7)
444 return false;
445
446 char tmp[3];
447 s->read(data: tmp, maxlen: 3); // padding
448 s->read(data: (char *)&length, maxlen: 4);
449 length = ntohl(netlong: length);
450
451 return true;
452}
453
454void QRfbRawEncoder::write()
455{
456// QVncDirtyMap *map = server->dirtyMap();
457 QTcpSocket *socket = client->clientSocket();
458
459 const int bytesPerPixel = client->clientBytesPerPixel();
460
461 // create a region from the dirty rects and send the region's merged rects.
462 // ### use the tile map again
463 QRegion rgn = client->dirtyRegion();
464 qCDebug(lcVnc) << "QRfbRawEncoder::write()" << rgn;
465// if (map) {
466// for (int y = 0; y < map->mapHeight; ++y) {
467// for (int x = 0; x < map->mapWidth; ++x) {
468// if (!map->dirty(x, y))
469// continue;
470// rgn += QRect(x * MAP_TILE_SIZE, y * MAP_TILE_SIZE,
471// MAP_TILE_SIZE, MAP_TILE_SIZE);
472// map->setClean(x, y);
473// }
474// }
475
476// rgn &= QRect(0, 0, server->screen()->geometry().width(),
477// server->screen()->geometry().height());
478// }
479
480 const QImage screenImage = client->server()->screenImage();
481 rgn &= screenImage.rect();
482
483 const auto rectsInRegion = rgn.rectCount();
484
485 {
486 const char tmp[2] = { 0, 0 }; // msg type, padding
487 socket->write(data: tmp, len: sizeof(tmp));
488 }
489
490 {
491 const quint16 count = htons(hostshort: rectsInRegion);
492 socket->write(data: (char *)&count, len: sizeof(count));
493 }
494
495 if (rectsInRegion <= 0)
496 return;
497
498 for (const QRect &tileRect: rgn) {
499 const QRfbRect rect(tileRect.x(), tileRect.y(),
500 tileRect.width(), tileRect.height());
501 rect.write(s: socket);
502
503 const quint32 encoding = htonl(hostlong: 0); // raw encoding
504 socket->write(data: (char *)&encoding, len: sizeof(encoding));
505
506 int linestep = screenImage.bytesPerLine();
507 const uchar *screendata = screenImage.scanLine(rect.y)
508 + rect.x * screenImage.depth() / 8;
509
510 if (client->doPixelConversion()) {
511 const int bufferSize = rect.w * rect.h * bytesPerPixel;
512 if (bufferSize > buffer.size())
513 buffer.resize(size: bufferSize);
514
515 // convert pixels
516 char *b = buffer.data();
517 const int bstep = rect.w * bytesPerPixel;
518 const int depth = screenImage.depth();
519 for (int i = 0; i < rect.h; ++i) {
520 client->convertPixels(dst: b, src: (const char*)screendata, count: rect.w, depth);
521 screendata += linestep;
522 b += bstep;
523 }
524 socket->write(data: buffer.constData(), len: bufferSize);
525 } else {
526 for (int i = 0; i < rect.h; ++i) {
527 socket->write(data: (const char*)screendata, len: rect.w * bytesPerPixel);
528 screendata += linestep;
529 }
530 }
531 if (socket->state() == QAbstractSocket::UnconnectedState)
532 break;
533 }
534 socket->flush();
535}
536
537#if QT_CONFIG(cursor)
538QVncClientCursor::QVncClientCursor()
539{
540 QWindow *w = QGuiApplication::focusWindow();
541 QCursor c = w ? w->cursor() : QCursor(Qt::ArrowCursor);
542 changeCursor(widgetCursor: &c, window: nullptr);
543}
544
545QVncClientCursor::~QVncClientCursor()
546{
547}
548
549void QVncClientCursor::write(QVncClient *client) const
550{
551 QTcpSocket *socket = client->clientSocket();
552
553 // FramebufferUpdate header
554 {
555 const quint16 tmp[6] = { htons(hostshort: 0),
556 htons(hostshort: 1),
557 htons(hostshort: uint16_t(hotspot.x())), htons(hostshort: uint16_t(hotspot.y())),
558 htons(hostshort: uint16_t(cursor.width())),
559 htons(hostshort: uint16_t(cursor.height())) };
560 socket->write(data: (char*)tmp, len: sizeof(tmp));
561
562 const qint32 encoding = qToBigEndian(source: -239);
563 socket->write(data: (char*)(&encoding), len: sizeof(encoding));
564 }
565
566 if (cursor.isNull())
567 return;
568
569 // write pixels
570 Q_ASSERT(cursor.hasAlphaChannel());
571 const QImage img = cursor.convertToFormat(f: client->server()->screen()->format());
572 const int n = client->clientBytesPerPixel() * img.width();
573 const int depth = img.depth();
574 char *buffer = new char[n];
575 for (int i = 0; i < img.height(); ++i) {
576 client->convertPixels(dst: buffer, src: (const char*)img.scanLine(i), count: img.width(), depth);
577 socket->write(data: buffer, len: n);
578 }
579 delete[] buffer;
580
581 // write mask
582 const QImage bitmap = cursor.createAlphaMask().convertToFormat(f: QImage::Format_Mono);
583 Q_ASSERT(bitmap.depth() == 1);
584 Q_ASSERT(bitmap.size() == img.size());
585 const int width = (bitmap.width() + 7) / 8;
586 for (int i = 0; i < bitmap.height(); ++i)
587 socket->write(data: (const char*)bitmap.scanLine(i), len: width);
588}
589
590void QVncClientCursor::changeCursor(QCursor *widgetCursor, QWindow *window)
591{
592 Q_UNUSED(window);
593 const Qt::CursorShape shape = widgetCursor ? widgetCursor->shape() : Qt::ArrowCursor;
594
595 if (shape == Qt::BitmapCursor) {
596 // application supplied cursor
597 hotspot = widgetCursor->hotSpot();
598 cursor = widgetCursor->pixmap().toImage();
599 } else {
600 // system cursor
601 QPlatformCursorImage platformImage(nullptr, nullptr, 0, 0, 0, 0);
602 platformImage.set(shape);
603 cursor = *platformImage.image();
604 hotspot = platformImage.hotspot();
605 }
606 for (auto client : qAsConst(t&: clients))
607 client->setDirtyCursor();
608}
609
610void QVncClientCursor::addClient(QVncClient *client)
611{
612 if (!clients.contains(t: client)) {
613 clients.append(t: client);
614 // Force a cursor update when the client connects.
615 client->setDirtyCursor();
616 }
617}
618
619uint QVncClientCursor::removeClient(QVncClient *client)
620{
621 clients.removeOne(t: client);
622 return clients.count();
623}
624#endif // QT_CONFIG(cursor)
625
626QVncServer::QVncServer(QVncScreen *screen, quint16 port)
627 : qvnc_screen(screen)
628 , m_port(port)
629{
630 QMetaObject::invokeMethod(obj: this, member: "init", type: Qt::QueuedConnection);
631}
632
633void QVncServer::init()
634{
635 serverSocket = new QTcpServer(this);
636 if (!serverSocket->listen(address: QHostAddress::Any, port: m_port))
637 qWarning() << "QVncServer could not connect:" << serverSocket->errorString();
638 else
639 qWarning(msg: "QVncServer created on port %d", m_port);
640
641 connect(sender: serverSocket, SIGNAL(newConnection()), receiver: this, SLOT(newConnection()));
642
643}
644
645QVncServer::~QVncServer()
646{
647 qDeleteAll(c: clients);
648}
649
650void QVncServer::setDirty()
651{
652 for (auto client : qAsConst(t&: clients))
653 client->setDirty(qvnc_screen->dirtyRegion);
654
655 qvnc_screen->clearDirty();
656}
657
658
659void QVncServer::newConnection()
660{
661 auto clientSocket = serverSocket->nextPendingConnection();
662 clients.append(t: new QVncClient(clientSocket, this));
663
664 dirtyMap()->reset();
665
666 qCDebug(lcVnc) << "new Connection from: " << clientSocket->localAddress();
667
668 qvnc_screen->setPowerState(QPlatformScreen::PowerStateOn);
669}
670
671void QVncServer::discardClient(QVncClient *client)
672{
673 clients.removeOne(t: client);
674 client->deleteLater();
675 if (clients.isEmpty()) {
676 qvnc_screen->disableClientCursor(client);
677 qvnc_screen->setPowerState(QPlatformScreen::PowerStateOff);
678 }
679}
680
681inline QImage QVncServer::screenImage() const
682{
683 return *qvnc_screen->image();
684}
685
686QT_END_NAMESPACE
687

source code of qtbase/src/plugins/platforms/vnc/qvnc.cpp