From 2cf3696d9f11ae2aa2dba56a0774bea10d59a482 Mon Sep 17 00:00:00 2001 From: Andy Nichols Date: Wed, 15 Jun 2016 15:07:41 +0200 Subject: Support multiple connected clients in the VNC plugin Previously it was only possible for one client to connect at a time. Now it is possible for multiple clients to connect to the VNC server and view and interact with the application. Change-Id: I886583a3abea2955367bf2da490127b041b5c5fb Reviewed-by: Laszlo Agocs --- src/plugins/platforms/vnc/qvnc.cpp | 705 +++---------------------------- src/plugins/platforms/vnc/qvnc_p.h | 79 +--- src/plugins/platforms/vnc/qvncclient.cpp | 694 ++++++++++++++++++++++++++++++ src/plugins/platforms/vnc/qvncclient.h | 149 +++++++ src/plugins/platforms/vnc/qvncscreen.cpp | 30 +- src/plugins/platforms/vnc/qvncscreen.h | 9 +- src/plugins/platforms/vnc/vnc.pro | 14 +- 7 files changed, 951 insertions(+), 729 deletions(-) create mode 100644 src/plugins/platforms/vnc/qvncclient.cpp create mode 100644 src/plugins/platforms/vnc/qvncclient.h (limited to 'src/plugins/platforms') diff --git a/src/plugins/platforms/vnc/qvnc.cpp b/src/plugins/platforms/vnc/qvnc.cpp index 6b9c55e4db..c14088c2a1 100644 --- a/src/plugins/platforms/vnc/qvnc.cpp +++ b/src/plugins/platforms/vnc/qvnc.cpp @@ -38,18 +38,22 @@ ****************************************************************************/ #include "qvnc_p.h" #include "qvncscreen.h" +#include "qvncclient.h" #include "QtNetwork/qtcpserver.h" #include "QtNetwork/qtcpsocket.h" -#include -#include #include +#include +#include + #ifdef Q_OS_WIN #include #else #include #endif +QT_BEGIN_NAMESPACE + QVncDirtyMap::QVncDirtyMap(QVncScreen *screen) : screen(screen), bytesPerPixel(0), numDirty(0) { @@ -445,14 +449,13 @@ bool QRfbClientCutText::read(QTcpSocket *s) void QRfbRawEncoder::write() { // QVncDirtyMap *map = server->dirtyMap(); - QTcpSocket *socket = server->clientSocket(); + QTcpSocket *socket = client->clientSocket(); - const int bytesPerPixel = server->clientBytesPerPixel(); + const int bytesPerPixel = client->clientBytesPerPixel(); // create a region from the dirty rects and send the region's merged rects. // ### use the tile map again - QRegion rgn = server->screen()->dirtyRegion; - server->screen()->clearDirty(); + QRegion rgn = client->dirtyRegion(); QT_VNC_DEBUG() << "QRfbRawEncoder::write()" << rgn; // if (map) { // for (int y = 0; y < map->mapHeight; ++y) { @@ -483,7 +486,7 @@ void QRfbRawEncoder::write() if (rects.size() <= 0) return; - const QImage screenImage = server->screenImage(); + const QImage screenImage = client->server()->screenImage(); for (const QRect &tileRect: rects) { const QRfbRect rect(tileRect.x(), tileRect.y(), @@ -497,7 +500,7 @@ void QRfbRawEncoder::write() const uchar *screendata = screenImage.scanLine(rect.y) + rect.x * screenImage.depth() / 8; - if (server->doPixelConversion()) { + if (client->doPixelConversion()) { const int bufferSize = rect.w * rect.h * bytesPerPixel; if (bufferSize > buffer.size()) buffer.resize(bufferSize); @@ -506,7 +509,7 @@ void QRfbRawEncoder::write() char *b = buffer.data(); const int bstep = rect.w * bytesPerPixel; for (int i = 0; i < rect.h; ++i) { - server->convertPixels(b, (const char*)screendata, rect.w); + client->convertPixels(b, (const char*)screendata, rect.w); screendata += linestep; b += bstep; } @@ -523,8 +526,7 @@ void QRfbRawEncoder::write() socket->flush(); } -QVncClientCursor::QVncClientCursor(QVncServer *s) - : server(s) +QVncClientCursor::QVncClientCursor() { QWindow *w = QGuiApplication::focusWindow(); QCursor c = w ? w->cursor() : QCursor(Qt::ArrowCursor); @@ -535,9 +537,9 @@ QVncClientCursor::~QVncClientCursor() { } -void QVncClientCursor::write() const +void QVncClientCursor::write(QVncClient *client) const { - QTcpSocket *socket = server->clientSocket(); + QTcpSocket *socket = client->clientSocket(); // FramebufferUpdate header { @@ -557,11 +559,11 @@ void QVncClientCursor::write() const // write pixels Q_ASSERT(cursor.hasAlphaChannel()); - const QImage img = cursor.convertToFormat(server->screen()->format()); - const int n = server->clientBytesPerPixel() * img.width(); + const QImage img = cursor.convertToFormat(client->server()->screen()->format()); + const int n = client->clientBytesPerPixel() * img.width(); char *buffer = new char[n]; for (int i = 0; i < img.height(); ++i) { - server->convertPixels(buffer, (const char*)img.scanLine(i), img.width()); + client->convertPixels(buffer, (const char*)img.scanLine(i), img.width()); socket->write(buffer, n); } delete[] buffer; @@ -592,7 +594,20 @@ void QVncClientCursor::changeCursor(QCursor *widgetCursor, QWindow *window) cursor = *platformImage.image(); hotspot = platformImage.hotspot(); } - server->setDirtyCursor(); + for (auto client : clients) + client->setDirtyCursor(); +} + +void QVncClientCursor::addClient(QVncClient *client) +{ + if (!clients.contains(client)) + clients.append(client); +} + +uint QVncClientCursor::removeClient(QVncClient *client) +{ + clients.removeOne(client); + return clients.count(); } #endif @@ -600,30 +615,18 @@ void QVncClientCursor::changeCursor(QCursor *widgetCursor, QWindow *window) QVncServer::QVncServer(QVncScreen *screen) : qvnc_screen(screen) { - QTimer::singleShot(0, this, SLOT(init())); + QMetaObject::invokeMethod(this, "init", Qt::QueuedConnection); } QVncServer::QVncServer(QVncScreen *screen, int /*id*/) : qvnc_screen(screen) { - QTimer::singleShot(0, this, SLOT(init())); + QMetaObject::invokeMethod(this, "init", Qt::QueuedConnection); } void QVncServer::init() { const int port = 5900; - handleMsg = false; - client = 0; - encodingsPending = 0; - cutTextPending = 0; - keymod = 0; - state = Disconnected; - dirtyCursor = false; - - refreshRate = 25; - timer = new QTimer(this); - timer->setSingleShot(true); - connect(timer, SIGNAL(timeout()), this, SLOT(checkUpdate())); serverSocket = new QTcpServer(this); if (!serverSocket->listen(QHostAddress::Any, port)) @@ -632,657 +635,51 @@ void QVncServer::init() QT_VNC_DEBUG("QVncServer created on port %d", port); QT_VNC_DEBUG() << "running in thread" << thread() << QThread::currentThread(); - serverSocket->waitForNewConnection(-1); - newConnection(); - connect(serverSocket, SIGNAL(newConnection()), this, SLOT(newConnection())); - encoder = 0; } QVncServer::~QVncServer() { - delete encoder; - encoder = 0; - delete client; - client = 0; + for (auto client : clients) { + delete client; + } } void QVncServer::setDirty() { - if (state == Connected && !timer->isActive() && - ((dirtyMap()->numDirty > 0) || dirtyCursor)) { - timer->start(); + for (auto client : clients) { + client->setDirty(qvnc_screen->dirtyRegion); } + qvnc_screen->clearDirty(); } + void QVncServer::newConnection() { - if (client) - delete client; + auto clientSocket = serverSocket->nextPendingConnection(); + clients.append(new QVncClient(clientSocket, this)); - client = serverSocket->nextPendingConnection(); - connect(client,SIGNAL(readyRead()),this,SLOT(readClient())); - connect(client,SIGNAL(disconnected()),this,SLOT(discardClient())); - handleMsg = false; - encodingsPending = 0; - cutTextPending = 0; - supportHextile = false; - wantUpdate = false; - - timer->start(1000 / refreshRate); dirtyMap()->reset(); - // send protocol version - const char *proto = "RFB 003.003\n"; - client->write(proto, 12); - state = Protocol; - QT_VNC_DEBUG() << "new Connection" << thread(); qvnc_screen->setPowerState(QPlatformScreen::PowerStateOn); } -void QVncServer::readClient() -{ - QT_VNC_DEBUG() << "readClient" << state; - switch (state) { - case Protocol: - if (client->bytesAvailable() >= 12) { - char proto[13]; - client->read(proto, 12); - proto[12] = '\0'; - QT_VNC_DEBUG("Client protocol version %s", proto); - // No authentication - quint32 auth = htonl(1); - client->write((char *) &auth, sizeof(auth)); - state = Init; - } - break; - - case Init: - if (client->bytesAvailable() >= 1) { - quint8 shared; - client->read((char *) &shared, 1); - - // Server Init msg - QRfbServerInit sim; - QRfbPixelFormat &format = sim.format; - switch (qvnc_screen->depth()) { - case 32: - format.bitsPerPixel = 32; - format.depth = 32; - format.bigEndian = 0; - format.trueColor = true; - format.redBits = 8; - format.greenBits = 8; - format.blueBits = 8; - format.redShift = 16; - format.greenShift = 8; - format.blueShift = 0; - break; - - case 24: - format.bitsPerPixel = 24; - format.depth = 24; - format.bigEndian = 0; - format.trueColor = true; - format.redBits = 8; - format.greenBits = 8; - format.blueBits = 8; - format.redShift = 16; - format.greenShift = 8; - format.blueShift = 0; - break; - - case 18: - format.bitsPerPixel = 24; - format.depth = 18; - format.bigEndian = 0; - format.trueColor = true; - format.redBits = 6; - format.greenBits = 6; - format.blueBits = 6; - format.redShift = 12; - format.greenShift = 6; - format.blueShift = 0; - break; - - case 16: - format.bitsPerPixel = 16; - format.depth = 16; - format.bigEndian = 0; - format.trueColor = true; - format.redBits = 5; - format.greenBits = 6; - format.blueBits = 5; - format.redShift = 11; - format.greenShift = 5; - format.blueShift = 0; - break; - - case 15: - format.bitsPerPixel = 16; - format.depth = 15; - format.bigEndian = 0; - format.trueColor = true; - format.redBits = 5; - format.greenBits = 5; - format.blueBits = 5; - format.redShift = 10; - format.greenShift = 5; - format.blueShift = 0; - break; - - case 12: - format.bitsPerPixel = 16; - format.depth = 12; - format.bigEndian = 0; - format.trueColor = true; - format.redBits = 4; - format.greenBits = 4; - format.blueBits = 4; - format.redShift = 8; - format.greenShift = 4; - format.blueShift = 0; - break; - - case 8: - case 4: - format.bitsPerPixel = 8; - format.depth = 8; - format.bigEndian = 0; - format.trueColor = false; - format.redBits = 0; - format.greenBits = 0; - format.blueBits = 0; - format.redShift = 0; - format.greenShift = 0; - format.blueShift = 0; - break; - - default: - qDebug("QVNC cannot drive depth %d", qvnc_screen->depth()); - discardClient(); - return; - } - sim.width = qvnc_screen->geometry().width(); - sim.height = qvnc_screen->geometry().height(); - sim.setName("Qt for Embedded Linux VNC Server"); - sim.write(client); - state = Connected; - } - break; - - case Connected: - do { - if (!handleMsg) { - client->read((char *)&msgType, 1); - handleMsg = true; - } - if (handleMsg) { - switch (msgType ) { - case SetPixelFormat: - setPixelFormat(); - break; - case FixColourMapEntries: - qDebug("Not supported: FixColourMapEntries"); - handleMsg = false; - break; - case SetEncodings: - setEncodings(); - break; - case FramebufferUpdateRequest: - frameBufferUpdateRequest(); - break; - case KeyEvent: - keyEvent(); - break; - case PointerEvent: - pointerEvent(); - break; - case ClientCutText: - clientCutText(); - break; - default: - qDebug("Unknown message type: %d", (int)msgType); - handleMsg = false; - } - } - } while (!handleMsg && client->bytesAvailable()); - break; - default: - break; - } -} - -#if Q_BYTE_ORDER == Q_BIG_ENDIAN -bool QVNCScreen::swapBytes() const -{ - if (depth() != 16) - return false; - - if (screen()) - return screen()->frameBufferLittleEndian(); - return frameBufferLittleEndian(); -} -#endif - -void QVncServer::setPixelFormat() +void QVncServer::discardClient(QVncClient *client) { - if (client->bytesAvailable() >= 19) { - char buf[3]; - client->read(buf, 3); // just padding - pixelFormat.read(client); - QT_VNC_DEBUG("Want format: %d %d %d %d %d %d %d %d %d %d", - int(pixelFormat.bitsPerPixel), - int(pixelFormat.depth), - int(pixelFormat.bigEndian), - int(pixelFormat.trueColor), - int(pixelFormat.redBits), - int(pixelFormat.greenBits), - int(pixelFormat.blueBits), - int(pixelFormat.redShift), - int(pixelFormat.greenShift), - int(pixelFormat.blueShift)); - if (!pixelFormat.trueColor) { - qDebug("Can only handle true color clients"); - discardClient(); - } - handleMsg = false; - sameEndian = (QSysInfo::ByteOrder == QSysInfo::BigEndian) == !!pixelFormat.bigEndian; - needConversion = pixelConversionNeeded(); -#if Q_BYTE_ORDER == Q_BIG_ENDIAN - swapBytes = qvnc_screen->swapBytes(); -#endif + clients.removeOne(client); + client->deleteLater(); + if (clients.isEmpty()) { + qvnc_screen->disableClientCursor(client); + qvnc_screen->setPowerState(QPlatformScreen::PowerStateOff); } } -void QVncServer::setEncodings() -{ - QRfbSetEncodings enc; - - if (!encodingsPending && enc.read(client)) { - encodingsPending = enc.count; - if (!encodingsPending) - handleMsg = false; - } - - if (encoder) { - delete encoder; - encoder = 0; - } - - enum Encodings { - Raw = 0, - CopyRect = 1, - RRE = 2, - CoRRE = 4, - Hextile = 5, - ZRLE = 16, - Cursor = -239, - DesktopSize = -223 - }; - - if (encodingsPending && (unsigned)client->bytesAvailable() >= - encodingsPending * sizeof(quint32)) { - for (int i = 0; i < encodingsPending; ++i) { - qint32 enc; - client->read((char *)&enc, sizeof(qint32)); - enc = ntohl(enc); - QT_VNC_DEBUG("QVncServer::setEncodings: %d", enc); - switch (enc) { - case Raw: - if (!encoder) { - encoder = new QRfbRawEncoder(this); - QT_VNC_DEBUG("QVncServer::setEncodings: using raw"); - } - break; - case CopyRect: - supportCopyRect = true; - break; - case RRE: - supportRRE = true; - break; - case CoRRE: - supportCoRRE = true; - break; - case Hextile: - supportHextile = true; - if (encoder) - break; -#if 0 - switch (qvnc_screen->depth()) { - case 8: - encoder = new QRfbHextileEncoder(this); - break; - case 16: - encoder = new QRfbHextileEncoder(this); - break; - case 32: - encoder = new QRfbHextileEncoder(this); - break; - default: - break; - } - QT_VNC_DEBUG("QVncServer::setEncodings: using hextile"); -#endif - break; - case ZRLE: - supportZRLE = true; - break; - case Cursor: - supportCursor = true; - qDebug() << "client side cursor supported."; - qvnc_screen->enableClientCursor(); - break; - case DesktopSize: - supportDesktopSize = true; - break; - default: - break; - } - } - handleMsg = false; - encodingsPending = 0; - } - - if (!encoder) { - encoder = new QRfbRawEncoder(this); - QT_VNC_DEBUG("QVncServer::setEncodings: fallback using raw"); - } -} - -void QVncServer::frameBufferUpdateRequest() -{ - QT_VNC_DEBUG() << "FramebufferUpdateRequest"; - QRfbFrameBufferUpdateRequest ev; - - if (ev.read(client)) { - if (!ev.incremental) { - QRect r(ev.rect.x, ev.rect.y, ev.rect.w, ev.rect.h); - r.translate(qvnc_screen->geometry().topLeft()); - qvnc_screen->setDirty(r); - } - wantUpdate = true; - checkUpdate(); - handleMsg = false; - } -} - -void QVncServer::pointerEvent() -{ - QRfbPointerEvent ev; - if (ev.read(client)) { - const QPoint pos = qvnc_screen->geometry().topLeft() + QPoint(ev.x, ev.y); - QWindowSystemInterface::handleMouseEvent(0, pos, pos, ev.buttons, QGuiApplication::keyboardModifiers()); - handleMsg = false; - } -} - -void QVncServer::keyEvent() -{ - QRfbKeyEvent ev; - - if (ev.read(client)) { - if (ev.keycode == Qt::Key_Shift) - keymod = ev.down ? keymod | Qt::ShiftModifier : - keymod & ~Qt::ShiftModifier; - else if (ev.keycode == Qt::Key_Control) - keymod = ev.down ? keymod | Qt::ControlModifier : - keymod & ~Qt::ControlModifier; - else if (ev.keycode == Qt::Key_Alt) - keymod = ev.down ? keymod | Qt::AltModifier : - keymod & ~Qt::AltModifier; - if (ev.unicode || ev.keycode) - QWindowSystemInterface::handleKeyEvent(0, ev.down ? QEvent::KeyPress : QEvent::KeyRelease, ev.keycode, keymod, QString(ev.unicode)); - handleMsg = false; - } -} - -void QVncServer::clientCutText() -{ - QRfbClientCutText ev; - - if (cutTextPending == 0 && ev.read(client)) { - cutTextPending = ev.length; - if (!cutTextPending) - handleMsg = false; - } - - if (cutTextPending && client->bytesAvailable() >= cutTextPending) { - char *text = new char [cutTextPending+1]; - client->read(text, cutTextPending); - delete [] text; - cutTextPending = 0; - handleMsg = false; - } -} - - -bool QVncServer::pixelConversionNeeded() const -{ - if (!sameEndian) - return true; - -#if Q_BYTE_ORDER == Q_BIG_ENDIAN - if (qvnc_screen->swapBytes()) - return true; -#endif - - const int screendepth = qvnc_screen->depth(); - if (screendepth != pixelFormat.bitsPerPixel) - return true; - - switch (screendepth) { - case 32: - case 24: - return false; - case 16: - return (pixelFormat.redBits == 5 - && pixelFormat.greenBits == 6 - && pixelFormat.blueBits == 5); - } - return true; -} - -// count: number of pixels -void QVncServer::convertPixels(char *dst, const char *src, int count) const -{ - const int screendepth = qvnc_screen->depth(); - - // cutoffs -#if Q_BYTE_ORDER == Q_BIG_ENDIAN - if (!swapBytes) -#endif - if (sameEndian) { - if (screendepth == pixelFormat.bitsPerPixel) { // memcpy cutoffs - - switch (screendepth) { - case 32: - memcpy(dst, src, count * sizeof(quint32)); - return; - case 16: - if (pixelFormat.redBits == 5 - && pixelFormat.greenBits == 6 - && pixelFormat.blueBits == 5) - { - memcpy(dst, src, count * sizeof(quint16)); - return; - } - } - } else if (screendepth == 16 && pixelFormat.bitsPerPixel == 32) { -#if defined(__i386__) // Currently fails on ARM if dst is not 4 byte aligned - const quint32 *src32 = reinterpret_cast(src); - quint32 *dst32 = reinterpret_cast(dst); - int count32 = count * sizeof(quint16) / sizeof(quint32); - while (count32--) { - const quint32 s = *src32++; - quint32 result1; - quint32 result2; - - // red - result1 = ((s & 0xf8000000) | ((s & 0xe0000000) >> 5)) >> 8; - result2 = ((s & 0x0000f800) | ((s & 0x0000e000) >> 5)) << 8; - - // green - result1 |= ((s & 0x07e00000) | ((s & 0x06000000) >> 6)) >> 11; - result2 |= ((s & 0x000007e0) | ((s & 0x00000600) >> 6)) << 5; - - // blue - result1 |= ((s & 0x001f0000) | ((s & 0x001c0000) >> 5)) >> 13; - result2 |= ((s & 0x0000001f) | ((s & 0x0000001c) >> 5)) << 3; - - *dst32++ = result2; - *dst32++ = result1; - } - if (count & 0x1) { - const quint16 *src16 = reinterpret_cast(src); - *dst32 = qt_conv16ToRgb(src16[count - 1]); - } - return; -#endif - } - } - - const int bytesPerPixel = (pixelFormat.bitsPerPixel + 7) / 8; - - for (int i = 0; i < count; ++i) { - int r, g, b; - - switch (screendepth) { - case 8: { - QRgb rgb = qvnc_screen->image()->colorTable()[int(*src)]; - r = qRed(rgb); - g = qGreen(rgb); - b = qBlue(rgb); - src++; - break; - } - case 16: { - quint16 p = *reinterpret_cast(src); -#if Q_BYTE_ORDER == Q_BIG_ENDIAN - if (swapBytes) - p = ((p & 0xff) << 8) | ((p & 0xff00) >> 8); -#endif - r = (p >> 11) & 0x1f; - g = (p >> 5) & 0x3f; - b = p & 0x1f; - r <<= 3; - g <<= 2; - b <<= 3; - src += sizeof(quint16); - break; - } - case 32: { - quint32 p = *reinterpret_cast(src); - r = (p >> 16) & 0xff; - g = (p >> 8) & 0xff; - b = p & 0xff; - src += sizeof(quint32); - break; - } - default: { - r = g = b = 0; - qDebug("QVNCServer: don't support %dbpp display", screendepth); - return; - } - } - -#if Q_BYTE_ORDER == Q_BIG_ENDIAN - if (swapBytes) - qSwap(r, b); -#endif - - r >>= (8 - pixelFormat.redBits); - g >>= (8 - pixelFormat.greenBits); - b >>= (8 - pixelFormat.blueBits); - - int pixel = (r << pixelFormat.redShift) | - (g << pixelFormat.greenShift) | - (b << pixelFormat.blueShift); - - if (sameEndian || pixelFormat.bitsPerPixel == 8) { - memcpy(dst, &pixel, bytesPerPixel); - dst += bytesPerPixel; - continue; - } - - - if (QSysInfo::ByteOrder == QSysInfo::BigEndian) { - switch (pixelFormat.bitsPerPixel) { - case 16: - pixel = (((pixel & 0x0000ff00) << 8) | - ((pixel & 0x000000ff) << 24)); - break; - case 32: - pixel = (((pixel & 0xff000000) >> 24) | - ((pixel & 0x00ff0000) >> 8) | - ((pixel & 0x0000ff00) << 8) | - ((pixel & 0x000000ff) << 24)); - break; - default: - qDebug("Cannot handle %d bpp client", pixelFormat.bitsPerPixel); - } - } else { // QSysInfo::ByteOrder == QSysInfo::LittleEndian - switch (pixelFormat.bitsPerPixel) { - case 16: - pixel = (((pixel & 0xff000000) >> 8) | - ((pixel & 0x00ff0000) << 8)); - break; - case 32: - pixel = (((pixel & 0xff000000) >> 24) | - ((pixel & 0x00ff0000) >> 8) | - ((pixel & 0x0000ff00) << 8) | - ((pixel & 0x000000ff) << 24)); - break; - default: - qDebug("Cannot handle %d bpp client", - pixelFormat.bitsPerPixel); - break; - } - } - memcpy(dst, &pixel, bytesPerPixel); - dst += bytesPerPixel; - } -} - -void QVncServer::checkUpdate() -{ - if (!wantUpdate) - return; - - if (dirtyCursor) { - qvnc_screen->clientCursor->write(); - dirtyCursor = false; - wantUpdate = false; - return; - } - - if (!qvnc_screen->dirtyRegion.isEmpty()) { - if (encoder) - encoder->write(); - wantUpdate = false; - } - // ### re-enable map support -// if (dirtyMap()->numDirty > 0) { -// if (encoder) -// encoder->write(); -// wantUpdate = false; -// } -} - -void QVncServer::discardClient() -{ - timer->stop(); - state = Disconnected; - delete encoder; - encoder = 0; - qvnc_screen->disableClientCursor(); - qvnc_screen->setPowerState(QPlatformScreen::PowerStateOff); -} - inline QImage QVncServer::screenImage() const { return *qvnc_screen->image(); } + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/vnc/qvnc_p.h b/src/plugins/platforms/vnc/qvnc_p.h index b67c1df838..dd8189d30f 100644 --- a/src/plugins/platforms/vnc/qvnc_p.h +++ b/src/plugins/platforms/vnc/qvnc_p.h @@ -53,12 +53,15 @@ #include #include +QT_BEGIN_NAMESPACE + class QTcpSocket; class QTcpServer; class QVncScreen; class QVncServer; class QVncClientCursor; +class QVncClient; // This fits with the VNC hextile messages #define MAP_TILE_SIZE 16 @@ -203,19 +206,19 @@ public: class QRfbEncoder { public: - QRfbEncoder(QVncServer *s) : server(s) {} + QRfbEncoder(QVncClient *s) : client(s) {} virtual ~QRfbEncoder() {} virtual void write() = 0; protected: - QVncServer *server; + QVncClient *client; }; class QRfbRawEncoder : public QRfbEncoder { public: - QRfbRawEncoder(QVncServer *s) : QRfbEncoder(s) {} + QRfbRawEncoder(QVncClient *s) : QRfbEncoder(s) {} void write(); @@ -368,16 +371,19 @@ private: class QVncClientCursor : public QPlatformCursor { public: - QVncClientCursor(QVncServer *s); + QVncClientCursor(); ~QVncClientCursor(); - void write() const; + void write(QVncClient *client) const; void changeCursor(QCursor *widgetCursor, QWindow *window); + void addClient(QVncClient *client); + uint removeClient(QVncClient *client); + QImage cursor; QPoint hotspot; - QVncServer *server; + QVector clients; }; @@ -389,80 +395,27 @@ public: QVncServer(QVncScreen *screen, int id); ~QVncServer(); - void setDirty(); - void setDirtyCursor() { dirtyCursor = true; setDirty(); } - inline bool isConnected() const { return state == Connected; } - inline void setRefreshRate(int rate) { refreshRate = rate; } - - enum ClientMsg { SetPixelFormat = 0, - FixColourMapEntries = 1, - SetEncodings = 2, - FramebufferUpdateRequest = 3, - KeyEvent = 4, - PointerEvent = 5, - ClientCutText = 6 }; - enum ServerMsg { FramebufferUpdate = 0, SetColourMapEntries = 1 }; - void convertPixels(char *dst, const char *src, int count) const; + void setDirty(); - inline int clientBytesPerPixel() const { - return pixelFormat.bitsPerPixel / 8; - } inline QVncScreen* screen() const { return qvnc_screen; } inline QVncDirtyMap* dirtyMap() const { return qvnc_screen->dirty; } - inline QTcpSocket* clientSocket() const { return client; } QImage screenImage() const; - inline bool doPixelConversion() const { return needConversion; } - -private: - void setPixelFormat(); - void setEncodings(); - void frameBufferUpdateRequest(); - void pointerEvent(); - void keyEvent(); - void clientCutText(); - bool pixelConversionNeeded() const; + void discardClient(QVncClient *client); private slots: void newConnection(); - void readClient(); - void checkUpdate(); - void discardClient(); void init(); private: - enum ClientState { Disconnected, Protocol, Init, Connected }; - QTimer *timer; QTcpServer *serverSocket; - QTcpSocket *client; - ClientState state; - quint8 msgType; - bool handleMsg; - QRfbPixelFormat pixelFormat; - Qt::KeyboardModifiers keymod; - int encodingsPending; - int cutTextPending; - uint supportCopyRect : 1; - uint supportRRE : 1; - uint supportCoRRE : 1; - uint supportHextile : 1; - uint supportZRLE : 1; - uint supportCursor : 1; - uint supportDesktopSize : 1; - bool wantUpdate; - bool sameEndian; - bool needConversion; -#if Q_BYTE_ORDER == Q_BIG_ENDIAN - bool swapBytes; -#endif - bool dirtyCursor; - int refreshRate; + QVector clients; QVncScreen *qvnc_screen; - QRfbEncoder *encoder; }; +QT_END_NAMESPACE #endif diff --git a/src/plugins/platforms/vnc/qvncclient.cpp b/src/plugins/platforms/vnc/qvncclient.cpp new file mode 100644 index 0000000000..55bef8c13e --- /dev/null +++ b/src/plugins/platforms/vnc/qvncclient.cpp @@ -0,0 +1,694 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qvncclient.h" +#include "qvnc_p.h" + +#include +#include + +#include +#include + +#ifdef Q_OS_WIN +#include +#else +#include +#endif + +QT_BEGIN_NAMESPACE + +QVncClient::QVncClient(QTcpSocket *clientSocket, QVncServer *server) + : QObject(server) + , m_server(server) + , m_clientSocket(clientSocket) + , m_encoder(nullptr) + , m_msgType(0) + , m_handleMsg(false) + , m_encodingsPending(0) + , m_cutTextPending(0) + , m_supportHextile(false) + , m_wantUpdate(false) + , m_keymod(0) + , m_dirtyCursor(false) + , m_updatePending(false) + , m_protocolVersion(V3_3) +{ + connect(m_clientSocket,SIGNAL(readyRead()),this,SLOT(readClient())); + connect(m_clientSocket,SIGNAL(disconnected()),this,SLOT(discardClient())); + + // send protocol version + const char *proto = "RFB 003.003\n"; + m_clientSocket->write(proto, 12); + m_state = Protocol; +} + +QVncClient::~QVncClient() +{ + delete m_encoder; +} + +QTcpSocket *QVncClient::clientSocket() const +{ + return m_clientSocket; +} + +void QVncClient::setDirty(const QRegion ®ion) +{ + m_dirtyRegion += region; + if (m_state == Connected && + ((m_server->dirtyMap()->numDirty > 0) || m_dirtyCursor)) { + scheduleUpdate(); + } +} + +void QVncClient::convertPixels(char *dst, const char *src, int count) const +{ + const int screendepth = m_server->screen()->depth(); + + // cutoffs +#if Q_BYTE_ORDER == Q_BIG_ENDIAN + if (!m_swapBytes) +#endif + if (m_sameEndian) { + if (screendepth == m_pixelFormat.bitsPerPixel) { // memcpy cutoffs + + switch (screendepth) { + case 32: + memcpy(dst, src, count * sizeof(quint32)); + return; + case 16: + if (m_pixelFormat.redBits == 5 + && m_pixelFormat.greenBits == 6 + && m_pixelFormat.blueBits == 5) + { + memcpy(dst, src, count * sizeof(quint16)); + return; + } + } + } else if (screendepth == 16 && m_pixelFormat.bitsPerPixel == 32) { +#if defined(__i386__) // Currently fails on ARM if dst is not 4 byte aligned + const quint32 *src32 = reinterpret_cast(src); + quint32 *dst32 = reinterpret_cast(dst); + int count32 = count * sizeof(quint16) / sizeof(quint32); + while (count32--) { + const quint32 s = *src32++; + quint32 result1; + quint32 result2; + + // red + result1 = ((s & 0xf8000000) | ((s & 0xe0000000) >> 5)) >> 8; + result2 = ((s & 0x0000f800) | ((s & 0x0000e000) >> 5)) << 8; + + // green + result1 |= ((s & 0x07e00000) | ((s & 0x06000000) >> 6)) >> 11; + result2 |= ((s & 0x000007e0) | ((s & 0x00000600) >> 6)) << 5; + + // blue + result1 |= ((s & 0x001f0000) | ((s & 0x001c0000) >> 5)) >> 13; + result2 |= ((s & 0x0000001f) | ((s & 0x0000001c) >> 5)) << 3; + + *dst32++ = result2; + *dst32++ = result1; + } + if (count & 0x1) { + const quint16 *src16 = reinterpret_cast(src); + *dst32 = qt_conv16ToRgb(src16[count - 1]); + } + return; +#endif + } + } + + const int bytesPerPixel = (m_pixelFormat.bitsPerPixel + 7) / 8; + + for (int i = 0; i < count; ++i) { + int r, g, b; + + switch (screendepth) { + case 8: { + QRgb rgb = m_server->screen()->image()->colorTable()[int(*src)]; + r = qRed(rgb); + g = qGreen(rgb); + b = qBlue(rgb); + src++; + break; + } + case 16: { + quint16 p = *reinterpret_cast(src); +#if Q_BYTE_ORDER == Q_BIG_ENDIAN + if (swapBytes) + p = ((p & 0xff) << 8) | ((p & 0xff00) >> 8); +#endif + r = (p >> 11) & 0x1f; + g = (p >> 5) & 0x3f; + b = p & 0x1f; + r <<= 3; + g <<= 2; + b <<= 3; + src += sizeof(quint16); + break; + } + case 32: { + quint32 p = *reinterpret_cast(src); + r = (p >> 16) & 0xff; + g = (p >> 8) & 0xff; + b = p & 0xff; + src += sizeof(quint32); + break; + } + default: { + r = g = b = 0; + qDebug("QVNCServer: don't support %dbpp display", screendepth); + return; + } + } + +#if Q_BYTE_ORDER == Q_BIG_ENDIAN + if (m_swapBytes) + qSwap(r, b); +#endif + + r >>= (8 - m_pixelFormat.redBits); + g >>= (8 - m_pixelFormat.greenBits); + b >>= (8 - m_pixelFormat.blueBits); + + int pixel = (r << m_pixelFormat.redShift) | + (g << m_pixelFormat.greenShift) | + (b << m_pixelFormat.blueShift); + + if (m_sameEndian || m_pixelFormat.bitsPerPixel == 8) { + memcpy(dst, &pixel, bytesPerPixel); + dst += bytesPerPixel; + continue; + } + + + if (QSysInfo::ByteOrder == QSysInfo::BigEndian) { + switch (m_pixelFormat.bitsPerPixel) { + case 16: + pixel = (((pixel & 0x0000ff00) << 8) | + ((pixel & 0x000000ff) << 24)); + break; + case 32: + pixel = (((pixel & 0xff000000) >> 24) | + ((pixel & 0x00ff0000) >> 8) | + ((pixel & 0x0000ff00) << 8) | + ((pixel & 0x000000ff) << 24)); + break; + default: + qDebug("Cannot handle %d bpp client", m_pixelFormat.bitsPerPixel); + } + } else { // QSysInfo::ByteOrder == QSysInfo::LittleEndian + switch (m_pixelFormat.bitsPerPixel) { + case 16: + pixel = (((pixel & 0xff000000) >> 8) | + ((pixel & 0x00ff0000) << 8)); + break; + case 32: + pixel = (((pixel & 0xff000000) >> 24) | + ((pixel & 0x00ff0000) >> 8) | + ((pixel & 0x0000ff00) << 8) | + ((pixel & 0x000000ff) << 24)); + break; + default: + qDebug("Cannot handle %d bpp client", + m_pixelFormat.bitsPerPixel); + break; + } + } + memcpy(dst, &pixel, bytesPerPixel); + dst += bytesPerPixel; + } +} + +void QVncClient::readClient() +{ + QT_VNC_DEBUG() << "readClient" << m_state; + switch (m_state) { + case Disconnected: + + break; + case Protocol: + if (m_clientSocket->bytesAvailable() >= 12) { + char proto[13]; + m_clientSocket->read(proto, 12); + proto[12] = '\0'; + QT_VNC_DEBUG("Client protocol version %s", proto); + if (!strcmp(proto, "RFB 003.008\n")) { + m_protocolVersion = V3_8; + } else if (!strcmp(proto, "RFB 003.007\n")) { + m_protocolVersion = V3_7; + } else { + m_protocolVersion = V3_3; + } + + if (m_protocolVersion == V3_3) { + // No authentication + quint32 auth = htonl(1); + m_clientSocket->write((char *) &auth, sizeof(auth)); + m_state = Init; + } + } + break; + case Authentication: + + break; + case Init: + if (m_clientSocket->bytesAvailable() >= 1) { + quint8 shared; + m_clientSocket->read((char *) &shared, 1); + + // Server Init msg + QRfbServerInit sim; + QRfbPixelFormat &format = sim.format; + switch (m_server->screen()->depth()) { + case 32: + format.bitsPerPixel = 32; + format.depth = 32; + format.bigEndian = 0; + format.trueColor = true; + format.redBits = 8; + format.greenBits = 8; + format.blueBits = 8; + format.redShift = 16; + format.greenShift = 8; + format.blueShift = 0; + break; + + case 24: + format.bitsPerPixel = 24; + format.depth = 24; + format.bigEndian = 0; + format.trueColor = true; + format.redBits = 8; + format.greenBits = 8; + format.blueBits = 8; + format.redShift = 16; + format.greenShift = 8; + format.blueShift = 0; + break; + + case 18: + format.bitsPerPixel = 24; + format.depth = 18; + format.bigEndian = 0; + format.trueColor = true; + format.redBits = 6; + format.greenBits = 6; + format.blueBits = 6; + format.redShift = 12; + format.greenShift = 6; + format.blueShift = 0; + break; + + case 16: + format.bitsPerPixel = 16; + format.depth = 16; + format.bigEndian = 0; + format.trueColor = true; + format.redBits = 5; + format.greenBits = 6; + format.blueBits = 5; + format.redShift = 11; + format.greenShift = 5; + format.blueShift = 0; + break; + + case 15: + format.bitsPerPixel = 16; + format.depth = 15; + format.bigEndian = 0; + format.trueColor = true; + format.redBits = 5; + format.greenBits = 5; + format.blueBits = 5; + format.redShift = 10; + format.greenShift = 5; + format.blueShift = 0; + break; + + case 12: + format.bitsPerPixel = 16; + format.depth = 12; + format.bigEndian = 0; + format.trueColor = true; + format.redBits = 4; + format.greenBits = 4; + format.blueBits = 4; + format.redShift = 8; + format.greenShift = 4; + format.blueShift = 0; + break; + + case 8: + case 4: + format.bitsPerPixel = 8; + format.depth = 8; + format.bigEndian = 0; + format.trueColor = false; + format.redBits = 0; + format.greenBits = 0; + format.blueBits = 0; + format.redShift = 0; + format.greenShift = 0; + format.blueShift = 0; + break; + + default: + qDebug("QVNC cannot drive depth %d", m_server->screen()->depth()); + discardClient(); + return; + } + sim.width = m_server->screen()->geometry().width(); + sim.height = m_server->screen()->geometry().height(); + sim.setName("Qt for Embedded Linux VNC Server"); + sim.write(m_clientSocket); + m_state = Connected; + } + break; + + case Connected: + do { + if (!m_handleMsg) { + m_clientSocket->read((char *)&m_msgType, 1); + m_handleMsg = true; + } + if (m_handleMsg) { + switch (m_msgType ) { + case SetPixelFormat: + setPixelFormat(); + break; + case FixColourMapEntries: + qDebug("Not supported: FixColourMapEntries"); + m_handleMsg = false; + break; + case SetEncodings: + setEncodings(); + break; + case FramebufferUpdateRequest: + frameBufferUpdateRequest(); + break; + case KeyEvent: + keyEvent(); + break; + case PointerEvent: + pointerEvent(); + break; + case ClientCutText: + clientCutText(); + break; + default: + qDebug("Unknown message type: %d", (int)m_msgType); + m_handleMsg = false; + } + } + } while (!m_handleMsg && m_clientSocket->bytesAvailable()); + break; + default: + break; + } +} + +void QVncClient::discardClient() +{ + m_state = Disconnected; + m_server->discardClient(this); +} + +void QVncClient::checkUpdate() +{ + if (!m_wantUpdate) + return; + + if (m_dirtyCursor) { + m_server->screen()->clientCursor->write(this); + m_dirtyCursor = false; + m_wantUpdate = false; + return; + } + + if (!m_dirtyRegion.isEmpty()) { + if (m_encoder) + m_encoder->write(); + m_wantUpdate = false; + m_dirtyRegion = QRegion(); + } +} + +void QVncClient::scheduleUpdate() +{ + if (!m_updatePending) { + m_updatePending = true; + QCoreApplication::postEvent(this, new QEvent(QEvent::UpdateRequest)); + } +} + +bool QVncClient::event(QEvent *event) +{ + if (event->type() == QEvent::UpdateRequest) { + m_updatePending = false; + checkUpdate(); + return true; + } + return QObject::event(event); +} + +void QVncClient::setPixelFormat() +{ + if (m_clientSocket->bytesAvailable() >= 19) { + char buf[3]; + m_clientSocket->read(buf, 3); // just padding + m_pixelFormat.read(m_clientSocket); + QT_VNC_DEBUG("Want format: %d %d %d %d %d %d %d %d %d %d", + int(m_pixelFormat.bitsPerPixel), + int(m_pixelFormat.depth), + int(m_pixelFormat.bigEndian), + int(m_pixelFormat.trueColor), + int(m_pixelFormat.redBits), + int(m_pixelFormat.greenBits), + int(m_pixelFormat.blueBits), + int(m_pixelFormat.redShift), + int(m_pixelFormat.greenShift), + int(m_pixelFormat.blueShift)); + if (!m_pixelFormat.trueColor) { + qDebug("Can only handle true color clients"); + discardClient(); + } + m_handleMsg = false; + m_sameEndian = (QSysInfo::ByteOrder == QSysInfo::BigEndian) == !!m_pixelFormat.bigEndian; + m_needConversion = pixelConversionNeeded(); +#if Q_BYTE_ORDER == Q_BIG_ENDIAN + m_swapBytes = qvnc_screen->swapBytes(); +#endif + } +} + +void QVncClient::setEncodings() +{ + QRfbSetEncodings enc; + + if (!m_encodingsPending && enc.read(m_clientSocket)) { + m_encodingsPending = enc.count; + if (!m_encodingsPending) + m_handleMsg = false; + } + + if (m_encoder) { + delete m_encoder; + m_encoder = nullptr; + } + + enum Encodings { + Raw = 0, + CopyRect = 1, + RRE = 2, + CoRRE = 4, + Hextile = 5, + ZRLE = 16, + Cursor = -239, + DesktopSize = -223 + }; + + if (m_encodingsPending && (unsigned)m_clientSocket->bytesAvailable() >= + m_encodingsPending * sizeof(quint32)) { + for (int i = 0; i < m_encodingsPending; ++i) { + qint32 enc; + m_clientSocket->read((char *)&enc, sizeof(qint32)); + enc = ntohl(enc); + QT_VNC_DEBUG("QVncServer::setEncodings: %d", enc); + switch (enc) { + case Raw: + if (!m_encoder) { + m_encoder = new QRfbRawEncoder(this); + QT_VNC_DEBUG("QVncServer::setEncodings: using raw"); + } + break; + case CopyRect: + m_supportCopyRect = true; + break; + case RRE: + m_supportRRE = true; + break; + case CoRRE: + m_supportCoRRE = true; + break; + case Hextile: + m_supportHextile = true; + if (m_encoder) + break; + break; + case ZRLE: + m_supportZRLE = true; + break; + case Cursor: + m_supportCursor = true; + qDebug() << "client side cursor supported."; + m_server->screen()->enableClientCursor(this); + break; + case DesktopSize: + m_supportDesktopSize = true; + break; + default: + break; + } + } + m_handleMsg = false; + m_encodingsPending = 0; + } + + if (!m_encoder) { + m_encoder = new QRfbRawEncoder(this); + QT_VNC_DEBUG("QVncServer::setEncodings: fallback using raw"); + } +} + +void QVncClient::frameBufferUpdateRequest() +{ + QT_VNC_DEBUG() << "FramebufferUpdateRequest"; + QRfbFrameBufferUpdateRequest ev; + + if (ev.read(m_clientSocket)) { + if (!ev.incremental) { + QRect r(ev.rect.x, ev.rect.y, ev.rect.w, ev.rect.h); + r.translate(m_server->screen()->geometry().topLeft()); + setDirty(r); + } + m_wantUpdate = true; + checkUpdate(); + m_handleMsg = false; + } +} + +void QVncClient::pointerEvent() +{ + QRfbPointerEvent ev; + if (ev.read(m_clientSocket)) { + const QPoint pos = m_server->screen()->geometry().topLeft() + QPoint(ev.x, ev.y); + QWindowSystemInterface::handleMouseEvent(0, pos, pos, ev.buttons, QGuiApplication::keyboardModifiers()); + m_handleMsg = false; + } +} + +void QVncClient::keyEvent() +{ + QRfbKeyEvent ev; + + if (ev.read(m_clientSocket)) { + if (ev.keycode == Qt::Key_Shift) + m_keymod = ev.down ? m_keymod | Qt::ShiftModifier : + m_keymod & ~Qt::ShiftModifier; + else if (ev.keycode == Qt::Key_Control) + m_keymod = ev.down ? m_keymod | Qt::ControlModifier : + m_keymod & ~Qt::ControlModifier; + else if (ev.keycode == Qt::Key_Alt) + m_keymod = ev.down ? m_keymod | Qt::AltModifier : + m_keymod & ~Qt::AltModifier; + if (ev.unicode || ev.keycode) + QWindowSystemInterface::handleKeyEvent(0, ev.down ? QEvent::KeyPress : QEvent::KeyRelease, ev.keycode, m_keymod, QString(ev.unicode)); + m_handleMsg = false; + } +} + +void QVncClient::clientCutText() +{ + QRfbClientCutText ev; + + if (m_cutTextPending == 0 && ev.read(m_clientSocket)) { + m_cutTextPending = ev.length; + if (!m_cutTextPending) + m_handleMsg = false; + } + + if (m_cutTextPending && m_clientSocket->bytesAvailable() >= m_cutTextPending) { + char *text = new char [m_cutTextPending+1]; + m_clientSocket->read(text, m_cutTextPending); + delete [] text; + m_cutTextPending = 0; + m_handleMsg = false; + } +} + +bool QVncClient::pixelConversionNeeded() const +{ + if (!m_sameEndian) + return true; + +#if Q_BYTE_ORDER == Q_BIG_ENDIAN + if (qvnc_screen->swapBytes()) + return true; +#endif + + const int screendepth = m_server->screen()->depth(); + if (screendepth != m_pixelFormat.bitsPerPixel) + return true; + + switch (screendepth) { + case 32: + case 24: + return false; + case 16: + return (m_pixelFormat.redBits == 5 + && m_pixelFormat.greenBits == 6 + && m_pixelFormat.blueBits == 5); + } + return true; +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/vnc/qvncclient.h b/src/plugins/platforms/vnc/qvncclient.h new file mode 100644 index 0000000000..a7a6b6b361 --- /dev/null +++ b/src/plugins/platforms/vnc/qvncclient.h @@ -0,0 +1,149 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QVNCCLIENT_H +#define QVNCCLIENT_H + +#include + +#include "qvnc_p.h" + +QT_BEGIN_NAMESPACE + +class QTcpSocket; +class QVncServer; + +class QVncClient : public QObject +{ + Q_OBJECT +public: + enum ClientMsg { + SetPixelFormat = 0, + FixColourMapEntries = 1, + SetEncodings = 2, + FramebufferUpdateRequest = 3, + KeyEvent = 4, + PointerEvent = 5, + ClientCutText = 6 + }; + + explicit QVncClient(QTcpSocket *clientSocket, QVncServer *server); + ~QVncClient(); + QTcpSocket *clientSocket() const; + QVncServer *server() const { return m_server; } + + void setDirty(const QRegion ®ion); + void setDirtyCursor() { m_dirtyCursor = true; scheduleUpdate(); } + QRegion dirtyRegion() const { return m_dirtyRegion; } + inline bool isConnected() const { return m_state == Connected; } + + inline int clientBytesPerPixel() const { + return m_pixelFormat.bitsPerPixel / 8; + } + + void convertPixels(char *dst, const char *src, int count) const; + inline bool doPixelConversion() const { return m_needConversion; } + +signals: + +private slots: + void readClient(); + void discardClient(); + void checkUpdate(); + void scheduleUpdate(); + +protected: + bool event(QEvent *event) override; + +private: + enum ClientState { + Disconnected, + Protocol, + Authentication, + Init, + Connected + }; + enum ProtocolVersion { + V3_3, + V3_7, + V3_8 + }; + + void setPixelFormat(); + void setEncodings(); + void frameBufferUpdateRequest(); + void pointerEvent(); + void keyEvent(); + void clientCutText(); + bool pixelConversionNeeded() const; + + QVncServer *m_server; + QTcpSocket *m_clientSocket; + QRfbEncoder *m_encoder; + + // Client State + ClientState m_state; + quint8 m_msgType; + bool m_handleMsg; + QRfbPixelFormat m_pixelFormat; + bool m_sameEndian; + bool m_needConversion; + int m_encodingsPending; + int m_cutTextPending; + uint m_supportCopyRect : 1; + uint m_supportRRE : 1; + uint m_supportCoRRE : 1; + uint m_supportHextile : 1; + uint m_supportZRLE : 1; + uint m_supportCursor : 1; + uint m_supportDesktopSize : 1; + bool m_wantUpdate; + Qt::KeyboardModifiers m_keymod; + bool m_dirtyCursor; + bool m_updatePending; +#if Q_BYTE_ORDER == Q_BIG_ENDIAN + bool m_swapBytes; +#endif + QRegion m_dirtyRegion; + ProtocolVersion m_protocolVersion; +}; + +QT_END_NAMESPACE + +#endif // QVNCCLIENT_H diff --git a/src/plugins/platforms/vnc/qvncscreen.cpp b/src/plugins/platforms/vnc/qvncscreen.cpp index b2ed898dcc..2b38ce6c51 100644 --- a/src/plugins/platforms/vnc/qvncscreen.cpp +++ b/src/plugins/platforms/vnc/qvncscreen.cpp @@ -70,8 +70,6 @@ bool QVncScreen::initialize() QFbScreen::initializeCompositor(); QT_VNC_DEBUG() << "QVncScreen::init" << geometry(); - disableClientCursor(); - switch (depth()) { case 32: dirty = new QVncDirtyMapOptimized(this); @@ -107,19 +105,23 @@ QRegion QVncScreen::doRedraw() return touched; } -void QVncScreen::enableClientCursor() +void QVncScreen::enableClientCursor(QVncClient *client) { delete mCursor; - mCursor = 0; - clientCursor = new QVncClientCursor(vncServer); + mCursor = nullptr; + if (!clientCursor) + clientCursor = new QVncClientCursor(); + clientCursor->addClient(client); } -void QVncScreen::disableClientCursor() +void QVncScreen::disableClientCursor(QVncClient *client) { - if (vncServer && clientCursor) { + uint clientCount = clientCursor->removeClient(client); + if (clientCount == 0) { delete clientCursor; - clientCursor = 0; + clientCursor = nullptr; } + mCursor = new QFbCursor(this); } @@ -155,5 +157,17 @@ QPixmap QVncScreen::grabWindow(WId wid, int x, int y, int width, int height) con return QPixmap(); } +#if Q_BYTE_ORDER == Q_BIG_ENDIAN +bool QVNCScreen::swapBytes() const +{ + if (depth() != 16) + return false; + + if (screen()) + return screen()->frameBufferLittleEndian(); + return frameBufferLittleEndian(); +} +#endif + QT_END_NAMESPACE diff --git a/src/plugins/platforms/vnc/qvncscreen.h b/src/plugins/platforms/vnc/qvncscreen.h index db2d3ac959..e3c6651781 100644 --- a/src/plugins/platforms/vnc/qvncscreen.h +++ b/src/plugins/platforms/vnc/qvncscreen.h @@ -50,6 +50,7 @@ class QTcpSocket; class QVncServer; class QVncDirtyMap; class QVncClientCursor; +class QVncClient; class QVncScreen : public QFbScreen { @@ -65,12 +66,16 @@ public: QRegion doRedraw() Q_DECL_OVERRIDE; QImage *image() const { return mScreenImage; } - void enableClientCursor(); - void disableClientCursor(); + void enableClientCursor(QVncClient *client); + void disableClientCursor(QVncClient *client); QPlatformCursor *cursor() const Q_DECL_OVERRIDE; void clearDirty() { dirtyRegion = QRegion(); } +#if Q_BYTE_ORDER == Q_BIG_ENDIAN + bool swapBytes() const +#endif + QStringList mArgs; qreal dpiX = 96; diff --git a/src/plugins/platforms/vnc/vnc.pro b/src/plugins/platforms/vnc/vnc.pro index c860ddee28..9a1428ac39 100644 --- a/src/plugins/platforms/vnc/vnc.pro +++ b/src/plugins/platforms/vnc/vnc.pro @@ -7,8 +7,18 @@ load(qt_plugin) QT += core-private gui-private platformsupport-private network -SOURCES = main.cpp qvncintegration.cpp qvncscreen.cpp qvnc.cpp -HEADERS = qvncintegration.h qvncscreen.h qvnc_p.h +SOURCES = \ + main.cpp \ + qvncintegration.cpp \ + qvncscreen.cpp \ + qvnc.cpp \ + qvncclient.cpp + +HEADERS = \ + qvncintegration.h \ + qvncscreen.h \ + qvnc_p.h \ + qvncclient.h CONFIG += qpa/genericunixfontdatabase -- cgit v1.2.3