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

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