| /**************************************************************************** |
| ** |
| ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). |
| ** All rights reserved. |
| ** Contact: Nokia Corporation (qt-info@nokia.com) |
| ** |
| ** This file is part of the plugins of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:LGPL$ |
| ** GNU Lesser General Public License Usage |
| ** This file may be used under the terms of the GNU Lesser General Public |
| ** License version 2.1 as published by the Free Software Foundation and |
| ** appearing in the file LICENSE.LGPL included in the packaging of this |
| ** file. Please review the following information to ensure the GNU Lesser |
| ** General Public License version 2.1 requirements will be met: |
| ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
| ** |
| ** In addition, as a special exception, Nokia gives you certain additional |
| ** rights. These rights are described in the Nokia Qt LGPL Exception |
| ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU General |
| ** Public License version 3.0 as published by the Free Software Foundation |
| ** and appearing in the file LICENSE.GPL included in the packaging of this |
| ** file. Please review the following information to ensure the GNU General |
| ** Public License version 3.0 requirements will be met: |
| ** http://www.gnu.org/copyleft/gpl.html. |
| ** |
| ** Other Usage |
| ** Alternatively, this file may be used in accordance with the terms and |
| ** conditions contained in a signed written agreement between you and Nokia. |
| ** |
| ** |
| ** |
| ** |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| #include "qscreenvnc_qws.h" |
| |
| #ifndef QT_NO_QWS_VNC |
| |
| #include "qscreenvnc_p.h" |
| #include "qwindowsystem_qws.h" |
| #include "qwsdisplay_qws.h" |
| #include "qscreendriverfactory_qws.h" |
| #include <QtCore/qtimer.h> |
| #include <QtCore/qregexp.h> |
| #include <QtGui/qwidget.h> |
| #include <QtGui/qpolygon.h> |
| #include <QtGui/qpainter.h> |
| #include <qdebug.h> |
| #include <private/qwindowsurface_qws_p.h> |
| #include <private/qwssignalhandler_p.h> |
| #include <private/qwidget_p.h> |
| #include <private/qdrawhelper_p.h> |
| |
| #include <stdlib.h> |
| |
| QT_BEGIN_NAMESPACE |
| |
| //#define QT_QWS_VNC_DEBUG |
| |
| extern QString qws_qtePipeFilename(); |
| |
| #ifndef QT_NO_QWS_CURSOR |
| |
| QVNCCursor::QVNCCursor(QVNCScreen *s) |
| : screen(s) |
| { |
| if (qt_screencursor) |
| setScreenCursor(qt_screencursor); |
| else |
| hwaccel = true; |
| } |
| |
| QVNCCursor::~QVNCCursor() |
| { |
| if (screenCursor()) |
| qt_screencursor = screenCursor(); |
| } |
| |
| void QVNCCursor::setDirty(const QRect &r) const |
| { |
| screen->d_ptr->setDirty(r, true); |
| } |
| |
| void QVNCCursor::hide() |
| { |
| QProxyScreenCursor::hide(); |
| if (enable) |
| setDirty(boundingRect()); |
| } |
| |
| void QVNCCursor::show() |
| { |
| QProxyScreenCursor::show(); |
| if (enable) |
| setDirty(boundingRect()); |
| } |
| |
| void QVNCCursor::set(const QImage &image, int hotx, int hoty) |
| { |
| QRegion dirty = boundingRect(); |
| QProxyScreenCursor::set(image, hotx, hoty); |
| dirty |= boundingRect(); |
| if (enable && hwaccel && !screen->d_ptr->vncServer->hasClientCursor()) { |
| const QVector<QRect> rects = dirty.rects(); |
| for (int i = 0; i < rects.size(); ++i) |
| setDirty(rects.at(i)); |
| } |
| } |
| |
| void QVNCCursor::move(int x, int y) |
| { |
| if (enable && hwaccel && !screen->d_ptr->vncServer->hasClientCursor()) { |
| QRegion dirty = boundingRect(); |
| QProxyScreenCursor::move(x, y); |
| dirty |= boundingRect(); |
| if (enable) { |
| const QVector<QRect> rects = dirty.rects(); |
| for (int i = 0; i < rects.size(); ++i) |
| setDirty(rects.at(i)); |
| } |
| } else { |
| QProxyScreenCursor::move(x, y); |
| } |
| } |
| |
| QVNCClientCursor::QVNCClientCursor(QVNCServer *s) |
| : server(s) |
| { |
| setScreenCursor(qt_screencursor); |
| Q_ASSERT(hwaccel); |
| qt_screencursor = this; // hw: XXX |
| |
| set(image(), hotspot.x(), hotspot.y()); |
| } |
| |
| QVNCClientCursor::~QVNCClientCursor() |
| { |
| qt_screencursor = screenCursor(); |
| } |
| |
| void QVNCClientCursor::set(const QImage &image, int hotx, int hoty) |
| { |
| QScreenCursor::set(image, hotx, hoty); |
| server->setDirtyCursor(); |
| } |
| |
| void QVNCClientCursor::write() const |
| { |
| QTcpSocket *socket = server->clientSocket(); |
| |
| // FramebufferUpdate header |
| { |
| const quint16 tmp[6] = { htons(0), |
| htons(1), |
| htons(hotspot.x()), htons(hotspot.y()), |
| htons(cursor.width()), |
| htons(cursor.height()) }; |
| socket->write((char*)tmp, sizeof(tmp)); |
| |
| const quint32 encoding = htonl(-239); |
| socket->write((char*)(&encoding), sizeof(encoding)); |
| } |
| |
| if (cursor.isNull()) |
| return; |
| |
| // write pixels |
| Q_ASSERT(cursor.hasAlphaChannel()); |
| const QImage img = cursor.convertToFormat(server->screen()->pixelFormat()); |
| const int n = server->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()); |
| socket->write(buffer, n); |
| } |
| delete[] buffer; |
| |
| // write mask |
| const QImage bitmap = cursor.createAlphaMask().convertToFormat(QImage::Format_Mono); |
| Q_ASSERT(bitmap.depth() == 1); |
| Q_ASSERT(bitmap.size() == img.size()); |
| const int width = (bitmap.width() + 7) / 8; |
| for (int i = 0; i < bitmap.height(); ++i) |
| socket->write((const char*)bitmap.scanLine(i), width); |
| } |
| |
| #endif // QT_NO_QWS_CURSOR |
| |
| QVNCScreenPrivate::QVNCScreenPrivate(QVNCScreen *parent) |
| : dpiX(72), dpiY(72), doOnScreenSurface(false), refreshRate(25), |
| vncServer(0), q_ptr(parent), noDisablePainting(false) |
| { |
| #ifdef QT_BUILD_INTERNAL |
| noDisablePainting = (qgetenv("QT_VNC_NO_DISABLEPAINTING").toInt() > 0); |
| #endif |
| #ifndef QT_NO_QWS_SIGNALHANDLER |
| QWSSignalHandler::instance()->addObject(this); |
| #endif |
| } |
| |
| QVNCScreenPrivate::~QVNCScreenPrivate() |
| { |
| #if defined(QT_NO_QWS_MULTIPROCESS) || defined(QT_NO_SHAREDMEMORY) |
| if (q_ptr->screen()) |
| return; |
| |
| delete[] q_ptr->data; |
| q_ptr->data = 0; |
| #else |
| shm.detach(); |
| #endif |
| } |
| |
| void QVNCScreenPrivate::configure() |
| { |
| if (q_ptr->screen()) |
| return; |
| |
| q_ptr->lstep = q_ptr->dw * ((q_ptr->d + 7) / 8); |
| q_ptr->size = q_ptr->h * q_ptr->lstep; |
| q_ptr->mapsize = q_ptr->size; |
| q_ptr->physWidth = qRound(q_ptr->dw * qreal(25.4) / dpiX); |
| q_ptr->physHeight = qRound(q_ptr->dh * qreal(25.4) / dpiY); |
| |
| switch (q_ptr->d) { |
| case 1: |
| q_ptr->setPixelFormat(QImage::Format_Mono); //### LSB??? |
| break; |
| case 8: |
| q_ptr->setPixelFormat(QImage::Format_Indexed8); |
| break; |
| case 12: |
| q_ptr->setPixelFormat(QImage::Format_RGB444); |
| break; |
| case 15: |
| q_ptr->setPixelFormat(QImage::Format_RGB555); |
| break; |
| case 16: |
| q_ptr->setPixelFormat(QImage::Format_RGB16); |
| break; |
| case 18: |
| q_ptr->setPixelFormat(QImage::Format_RGB666); |
| break; |
| case 24: |
| q_ptr->setPixelFormat(QImage::Format_RGB888); |
| break; |
| case 32: |
| q_ptr->setPixelFormat(QImage::Format_ARGB32_Premultiplied); |
| break; |
| } |
| |
| #if !defined(QT_NO_QWS_MULTIPROCESS) && !defined(QT_NO_SHAREDMEMORY) |
| if (q_ptr->size != shm.size()) { |
| shm.detach(); |
| const QString key = qws_qtePipeFilename() + |
| QString().sprintf("_vnc_%d_%d", |
| q_ptr->displayId, q_ptr->size); |
| shm.setKey(key); |
| if (QApplication::type() == QApplication::GuiServer) { |
| if (!shm.create(q_ptr->size)) { |
| qWarning() << "QVNCScreen could not create shared memory:" |
| << shm.errorString(); |
| if (!shm.attach()) { |
| qWarning() << "QVNCScreen could not attach to shared memory:" |
| << shm.errorString(); |
| } |
| } |
| } else if (!shm.attach()) { |
| qWarning() << "QVNCScreen could not attach to shared memory:" |
| << shm.errorString(); |
| } |
| q_ptr->data = reinterpret_cast<uchar*>(shm.data()); |
| } |
| #else |
| if (q_ptr->data) |
| delete[] q_ptr->data; |
| q_ptr->data = new uchar[q_ptr->size]; |
| #endif |
| } |
| |
| //=========================================================================== |
| |
| static const struct { |
| int keysym; |
| int keycode; |
| } keyMap[] = { |
| { 0xff08, Qt::Key_Backspace }, |
| { 0xff09, Qt::Key_Tab }, |
| { 0xff0d, Qt::Key_Return }, |
| { 0xff1b, Qt::Key_Escape }, |
| { 0xff63, Qt::Key_Insert }, |
| { 0xffff, Qt::Key_Delete }, |
| { 0xff50, Qt::Key_Home }, |
| { 0xff57, Qt::Key_End }, |
| { 0xff55, Qt::Key_PageUp }, |
| { 0xff56, Qt::Key_PageDown }, |
| { 0xff51, Qt::Key_Left }, |
| { 0xff52, Qt::Key_Up }, |
| { 0xff53, Qt::Key_Right }, |
| { 0xff54, Qt::Key_Down }, |
| { 0xffbe, Qt::Key_F1 }, |
| { 0xffbf, Qt::Key_F2 }, |
| { 0xffc0, Qt::Key_F3 }, |
| { 0xffc1, Qt::Key_F4 }, |
| { 0xffc2, Qt::Key_F5 }, |
| { 0xffc3, Qt::Key_F6 }, |
| { 0xffc4, Qt::Key_F7 }, |
| { 0xffc5, Qt::Key_F8 }, |
| { 0xffc6, Qt::Key_F9 }, |
| { 0xffc7, Qt::Key_F10 }, |
| { 0xffc8, Qt::Key_F11 }, |
| { 0xffc9, Qt::Key_F12 }, |
| { 0xffe1, Qt::Key_Shift }, |
| { 0xffe2, Qt::Key_Shift }, |
| { 0xffe3, Qt::Key_Control }, |
| { 0xffe4, Qt::Key_Control }, |
| { 0xffe7, Qt::Key_Meta }, |
| { 0xffe8, Qt::Key_Meta }, |
| { 0xffe9, Qt::Key_Alt }, |
| { 0xffea, Qt::Key_Alt }, |
| |
| { 0xffb0, Qt::Key_0 }, |
| { 0xffb1, Qt::Key_1 }, |
| { 0xffb2, Qt::Key_2 }, |
| { 0xffb3, Qt::Key_3 }, |
| { 0xffb4, Qt::Key_4 }, |
| { 0xffb5, Qt::Key_5 }, |
| { 0xffb6, Qt::Key_6 }, |
| { 0xffb7, Qt::Key_7 }, |
| { 0xffb8, Qt::Key_8 }, |
| { 0xffb9, Qt::Key_9 }, |
| |
| { 0xff8d, Qt::Key_Return }, |
| { 0xffaa, Qt::Key_Asterisk }, |
| { 0xffab, Qt::Key_Plus }, |
| { 0xffad, Qt::Key_Minus }, |
| { 0xffae, Qt::Key_Period }, |
| { 0xffaf, Qt::Key_Slash }, |
| |
| { 0xff95, Qt::Key_Home }, |
| { 0xff96, Qt::Key_Left }, |
| { 0xff97, Qt::Key_Up }, |
| { 0xff98, Qt::Key_Right }, |
| { 0xff99, Qt::Key_Down }, |
| { 0xff9a, Qt::Key_PageUp }, |
| { 0xff9b, Qt::Key_PageDown }, |
| { 0xff9c, Qt::Key_End }, |
| { 0xff9e, Qt::Key_Insert }, |
| { 0xff9f, Qt::Key_Delete }, |
| |
| { 0, 0 } |
| }; |
| |
| void QRfbRect::read(QTcpSocket *s) |
| { |
| quint16 buf[4]; |
| s->read((char*)buf, 8); |
| x = ntohs(buf[0]); |
| y = ntohs(buf[1]); |
| w = ntohs(buf[2]); |
| h = ntohs(buf[3]); |
| } |
| |
| void QRfbRect::write(QTcpSocket *s) const |
| { |
| quint16 buf[4]; |
| buf[0] = htons(x); |
| buf[1] = htons(y); |
| buf[2] = htons(w); |
| buf[3] = htons(h); |
| s->write((char*)buf, 8); |
| } |
| |
| void QRfbPixelFormat::read(QTcpSocket *s) |
| { |
| char buf[16]; |
| s->read(buf, 16); |
| bitsPerPixel = buf[0]; |
| depth = buf[1]; |
| bigEndian = buf[2]; |
| trueColor = buf[3]; |
| |
| quint16 a = ntohs(*(quint16 *)(buf + 4)); |
| redBits = 0; |
| while (a) { a >>= 1; redBits++; } |
| |
| a = ntohs(*(quint16 *)(buf + 6)); |
| greenBits = 0; |
| while (a) { a >>= 1; greenBits++; } |
| |
| a = ntohs(*(quint16 *)(buf + 8)); |
| blueBits = 0; |
| while (a) { a >>= 1; blueBits++; } |
| |
| redShift = buf[10]; |
| greenShift = buf[11]; |
| blueShift = buf[12]; |
| } |
| |
| void QRfbPixelFormat::write(QTcpSocket *s) |
| { |
| char buf[16]; |
| buf[0] = bitsPerPixel; |
| buf[1] = depth; |
| buf[2] = bigEndian; |
| buf[3] = trueColor; |
| |
| quint16 a = 0; |
| for (int i = 0; i < redBits; i++) a = (a << 1) | 1; |
| *(quint16 *)(buf + 4) = htons(a); |
| |
| a = 0; |
| for (int i = 0; i < greenBits; i++) a = (a << 1) | 1; |
| *(quint16 *)(buf + 6) = htons(a); |
| |
| a = 0; |
| for (int i = 0; i < blueBits; i++) a = (a << 1) | 1; |
| *(quint16 *)(buf + 8) = htons(a); |
| |
| buf[10] = redShift; |
| buf[11] = greenShift; |
| buf[12] = blueShift; |
| s->write(buf, 16); |
| } |
| |
| |
| void QRfbServerInit::setName(const char *n) |
| { |
| delete[] name; |
| name = new char [strlen(n) + 1]; |
| strcpy(name, n); |
| } |
| |
| void QRfbServerInit::read(QTcpSocket *s) |
| { |
| s->read((char *)&width, 2); |
| width = ntohs(width); |
| s->read((char *)&height, 2); |
| height = ntohs(height); |
| format.read(s); |
| |
| quint32 len; |
| s->read((char *)&len, 4); |
| len = ntohl(len); |
| |
| name = new char [len + 1]; |
| s->read(name, len); |
| name[len] = '\0'; |
| } |
| |
| void QRfbServerInit::write(QTcpSocket *s) |
| { |
| quint16 t = htons(width); |
| s->write((char *)&t, 2); |
| t = htons(height); |
| s->write((char *)&t, 2); |
| format.write(s); |
| quint32 len = strlen(name); |
| len = htonl(len); |
| s->write((char *)&len, 4); |
| s->write(name, strlen(name)); |
| } |
| |
| bool QRfbSetEncodings::read(QTcpSocket *s) |
| { |
| if (s->bytesAvailable() < 3) |
| return false; |
| |
| char tmp; |
| s->read(&tmp, 1); // padding |
| s->read((char *)&count, 2); |
| count = ntohs(count); |
| |
| return true; |
| } |
| |
| bool QRfbFrameBufferUpdateRequest::read(QTcpSocket *s) |
| { |
| if (s->bytesAvailable() < 9) |
| return false; |
| |
| s->read(&incremental, 1); |
| rect.read(s); |
| |
| return true; |
| } |
| |
| bool QRfbKeyEvent::read(QTcpSocket *s) |
| { |
| if (s->bytesAvailable() < 7) |
| return false; |
| |
| s->read(&down, 1); |
| quint16 tmp; |
| s->read((char *)&tmp, 2); // padding |
| |
| quint32 key; |
| s->read((char *)&key, 4); |
| key = ntohl(key); |
| |
| unicode = 0; |
| keycode = 0; |
| int i = 0; |
| while (keyMap[i].keysym && !keycode) { |
| if (keyMap[i].keysym == (int)key) |
| keycode = keyMap[i].keycode; |
| i++; |
| } |
| |
| if (keycode >= ' ' && keycode <= '~') |
| unicode = keycode; |
| |
| if (!keycode) { |
| if (key <= 0xff) { |
| unicode = key; |
| if (key >= 'a' && key <= 'z') |
| keycode = Qt::Key_A + key - 'a'; |
| else if (key >= ' ' && key <= '~') |
| keycode = Qt::Key_Space + key - ' '; |
| } |
| } |
| |
| return true; |
| } |
| |
| bool QRfbPointerEvent::read(QTcpSocket *s) |
| { |
| if (s->bytesAvailable() < 5) |
| return false; |
| |
| char buttonMask; |
| s->read(&buttonMask, 1); |
| buttons = 0; |
| if (buttonMask & 1) |
| buttons |= Qt::LeftButton; |
| if (buttonMask & 2) |
| buttons |= Qt::MidButton; |
| if (buttonMask & 4) |
| buttons |= Qt::RightButton; |
| |
| quint16 tmp; |
| s->read((char *)&tmp, 2); |
| x = ntohs(tmp); |
| s->read((char *)&tmp, 2); |
| y = ntohs(tmp); |
| |
| return true; |
| } |
| |
| bool QRfbClientCutText::read(QTcpSocket *s) |
| { |
| if (s->bytesAvailable() < 7) |
| return false; |
| |
| char tmp[3]; |
| s->read(tmp, 3); // padding |
| s->read((char *)&length, 4); |
| length = ntohl(length); |
| |
| return true; |
| } |
| |
| //=========================================================================== |
| |
| QVNCServer::QVNCServer(QVNCScreen *screen) |
| : qvnc_screen(screen) |
| { |
| init(5900); |
| } |
| |
| QVNCServer::QVNCServer(QVNCScreen *screen, int id) |
| : qvnc_screen(screen) |
| { |
| init(5900 + id); |
| } |
| |
| void QVNCServer::init(uint port) |
| { |
| handleMsg = false; |
| client = 0; |
| encodingsPending = 0; |
| cutTextPending = 0; |
| keymod = 0; |
| state = Unconnected; |
| 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)) |
| qDebug() << "QVNCServer could not connect:" << serverSocket->errorString(); |
| else |
| qDebug("QVNCServer created on port %d", port); |
| |
| connect(serverSocket, SIGNAL(newConnection()), this, SLOT(newConnection())); |
| |
| #ifndef QT_NO_QWS_CURSOR |
| qvnc_cursor = 0; |
| #endif |
| encoder = 0; |
| } |
| |
| QVNCServer::~QVNCServer() |
| { |
| delete encoder; |
| encoder = 0; |
| delete client; |
| client = 0; |
| #ifndef QT_NO_QWS_CURSOR |
| delete qvnc_cursor; |
| qvnc_cursor = 0; |
| #endif |
| } |
| |
| void QVNCServer::setDirty() |
| { |
| if (state == Connected && !timer->isActive() && |
| ((dirtyMap()->numDirty > 0) || dirtyCursor)) { |
| timer->start(); |
| } |
| } |
| |
| void QVNCServer::newConnection() |
| { |
| if (client) |
| delete client; |
| |
| 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; |
| |
| if (!qvnc_screen->screen() && !qvnc_screen->d_ptr->noDisablePainting) |
| QWSServer::instance()->enablePainting(true); |
| } |
| |
| void QVNCServer::readClient() |
| { |
| switch (state) { |
| case Protocol: |
| if (client->bytesAvailable() >= 12) { |
| char proto[13]; |
| client->read(proto, 12); |
| proto[12] = '\0'; |
| qDebug("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->deviceWidth(); |
| sim.height = qvnc_screen->deviceHeight(); |
| 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() |
| { |
| if (client->bytesAvailable() >= 19) { |
| char buf[3]; |
| client->read(buf, 3); // just padding |
| pixelFormat.read(client); |
| #ifdef QT_QWS_VNC_DEBUG |
| qDebug("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)); |
| #endif |
| 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 |
| } |
| } |
| |
| 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); |
| #ifdef QT_QWS_VNC_DEBUG |
| qDebug("QVNCServer::setEncodings: %d", enc); |
| #endif |
| switch (enc) { |
| case Raw: |
| if (!encoder) { |
| encoder = new QRfbRawEncoder(this); |
| #ifdef QT_QWS_VNC_DEBUG |
| qDebug("QVNCServer::setEncodings: using raw"); |
| #endif |
| } |
| break; |
| case CopyRect: |
| supportCopyRect = true; |
| break; |
| case RRE: |
| supportRRE = true; |
| break; |
| case CoRRE: |
| supportCoRRE = true; |
| break; |
| case Hextile: |
| supportHextile = true; |
| if (encoder) |
| break; |
| switch (qvnc_screen->depth()) { |
| #ifdef QT_QWS_DEPTH_8 |
| case 8: |
| encoder = new QRfbHextileEncoder<quint8>(this); |
| break; |
| #endif |
| #ifdef QT_QWS_DEPTH_12 |
| case 12: |
| encoder = new QRfbHextileEncoder<qrgb444>(this); |
| break; |
| #endif |
| #ifdef QT_QWS_DEPTH_15 |
| case 15: |
| encoder = new QRfbHextileEncoder<qrgb555>(this); |
| break; |
| #endif |
| #ifdef QT_QWS_DEPTH_16 |
| case 16: |
| encoder = new QRfbHextileEncoder<quint16>(this); |
| break; |
| #endif |
| #ifdef QT_QWS_DEPTH_18 |
| case 18: |
| encoder = new QRfbHextileEncoder<qrgb666>(this); |
| break; |
| #endif |
| #ifdef QT_QWS_DEPTH_24 |
| case 24: |
| encoder = new QRfbHextileEncoder<qrgb888>(this); |
| break; |
| #endif |
| #ifdef QT_QWS_DEPTH_32 |
| case 32: |
| encoder = new QRfbHextileEncoder<quint32>(this); |
| break; |
| #endif |
| default: |
| break; |
| } |
| #ifdef QT_QWS_VNC_DEBUG |
| qDebug("QVNCServer::setEncodings: using hextile"); |
| #endif |
| break; |
| case ZRLE: |
| supportZRLE = true; |
| break; |
| case Cursor: |
| supportCursor = true; |
| #ifndef QT_NO_QWS_CURSOR |
| if (!qvnc_screen->screen() || qt_screencursor->isAccelerated()) { |
| delete qvnc_cursor; |
| qvnc_cursor = new QVNCClientCursor(this); |
| } |
| #endif |
| break; |
| case DesktopSize: |
| supportDesktopSize = true; |
| break; |
| default: |
| break; |
| } |
| } |
| handleMsg = false; |
| encodingsPending = 0; |
| } |
| |
| if (!encoder) { |
| encoder = new QRfbRawEncoder(this); |
| #ifdef QT_QWS_VNC_DEBUG |
| qDebug("QVNCServer::setEncodings: fallback using raw"); |
| #endif |
| } |
| } |
| |
| void QVNCServer::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->offset()); |
| qvnc_screen->d_ptr->setDirty(r, true); |
| } |
| wantUpdate = true; |
| checkUpdate(); |
| handleMsg = false; |
| } |
| } |
| |
| void QVNCServer::pointerEvent() |
| { |
| QRfbPointerEvent ev; |
| if (ev.read(client)) { |
| const QPoint offset = qvnc_screen->offset(); |
| QWSServer::sendMouseEvent(offset + QPoint(ev.x, ev.y), ev.buttons); |
| 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) |
| QWSServer::sendKeyEvent(ev.unicode, ev.keycode, keymod, ev.down, false); |
| 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; |
| } |
| } |
| |
| // stride in bytes |
| template <class SRC> |
| bool QRfbSingleColorHextile<SRC>::read(const uchar *data, |
| int width, int height, int stride) |
| { |
| const int depth = encoder->server->screen()->depth(); |
| if (width % (depth / 8)) // hw: should rather fallback to simple loop |
| return false; |
| |
| static int alwaysFalse = qgetenv("QT_VNC_NOCHECKFILL").toInt(); |
| if (alwaysFalse) |
| return false; |
| |
| switch (depth) { |
| case 4: { |
| const quint8 *data8 = reinterpret_cast<const quint8*>(data); |
| if ((data8[0] & 0xf) != (data8[0] >> 4)) |
| return false; |
| width /= 2; |
| } // fallthrough |
| case 8: { |
| const quint8 *data8 = reinterpret_cast<const quint8*>(data); |
| if (data8[0] != data8[1]) |
| return false; |
| width /= 2; |
| } // fallthrough |
| case 12: |
| case 15: |
| case 16: { |
| const quint16 *data16 = reinterpret_cast<const quint16*>(data); |
| if (data16[0] != data16[1]) |
| return false; |
| width /= 2; |
| } // fallthrough |
| case 18: |
| case 24: |
| case 32: { |
| const quint32 *data32 = reinterpret_cast<const quint32*>(data); |
| const quint32 first = data32[0]; |
| const int linestep = (stride / sizeof(quint32)) - width; |
| for (int y = 0; y < height; ++y) { |
| for (int x = 0; x < width; ++x) { |
| if (*(data32++) != first) |
| return false; |
| } |
| data32 += linestep; |
| } |
| break; |
| } |
| default: |
| return false; |
| } |
| |
| SRC color = reinterpret_cast<const SRC*>(data)[0]; |
| encoder->newBg |= (color != encoder->bg); |
| encoder->bg = color; |
| return true; |
| } |
| |
| template <class SRC> |
| void QRfbSingleColorHextile<SRC>::write(QTcpSocket *socket) const |
| { |
| if (true || encoder->newBg) { |
| const int bpp = encoder->server->clientBytesPerPixel(); |
| const int padding = 3; |
| QVarLengthArray<char> buffer(padding + 1 + bpp); |
| buffer[padding] = 2; // BackgroundSpecified |
| encoder->server->convertPixels(buffer.data() + padding + 1, |
| reinterpret_cast<char*>(&encoder->bg), |
| 1); |
| socket->write(buffer.data() + padding, bpp + 1); |
| // encoder->newBg = false; |
| } else { |
| char subenc = 0; |
| socket->write(&subenc, 1); |
| } |
| } |
| |
| template <class SRC> |
| bool QRfbDualColorHextile<SRC>::read(const uchar *data, |
| int width, int height, int stride) |
| { |
| const SRC *ptr = reinterpret_cast<const SRC*>(data); |
| const int linestep = (stride / sizeof(SRC)) - width; |
| |
| SRC c1; |
| SRC c2 = 0; |
| int n1 = 0; |
| int n2 = 0; |
| int x = 0; |
| int y = 0; |
| |
| c1 = *ptr; |
| |
| // find second color |
| while (y < height) { |
| while (x < width) { |
| if (*ptr == c1) { |
| ++n1; |
| } else { |
| c2 = *ptr; |
| goto found_second_color; |
| } |
| ++ptr; |
| ++x; |
| } |
| x = 0; |
| ptr += linestep; |
| ++y; |
| } |
| |
| found_second_color: |
| // finish counting |
| while (y < height) { |
| while (x < width) { |
| if (*ptr == c1) { |
| ++n1; |
| } else if (*ptr == c2) { |
| ++n2; |
| } else { |
| return false; |
| } |
| ++ptr; |
| ++x; |
| } |
| x = 0; |
| ptr += linestep; |
| ++y; |
| } |
| |
| if (n2 > n1) { |
| const quint32 tmpC = c1; |
| c1 = c2; |
| c2 = tmpC; |
| } |
| |
| encoder->newBg |= (c1 != encoder->bg); |
| encoder->newFg |= (c2 != encoder->fg); |
| |
| encoder->bg = c1; |
| encoder->fg = c2; |
| |
| // create map |
| bool inRect = false; |
| numRects = 0; |
| ptr = reinterpret_cast<const SRC*>(data); |
| for (y = 0; y < height; ++y) { |
| for (x = 0; x < width; ++x) { |
| if (inRect && *ptr == encoder->bg) { |
| // rect finished |
| setWidth(x - lastx()); |
| next(); |
| inRect = false; |
| } else if (!inRect && *ptr == encoder->fg) { |
| // rect start |
| setX(x); |
| setY(y); |
| setHeight(1); |
| inRect = true; |
| } |
| ++ptr; |
| } |
| if (inRect) { |
| // finish rect |
| setWidth(width - lastx()); |
| next(); |
| inRect = false; |
| } |
| ptr += linestep; |
| } |
| |
| return true; |
| } |
| |
| template <class SRC> |
| void QRfbDualColorHextile<SRC>::write(QTcpSocket *socket) const |
| { |
| const int bpp = encoder->server->clientBytesPerPixel(); |
| const int padding = 3; |
| QVarLengthArray<char> buffer(padding + 2 * bpp + sizeof(char) + sizeof(numRects)); |
| char &subenc = buffer[padding]; |
| int n = padding + sizeof(subenc); |
| |
| subenc = 0x8; // AnySubrects |
| |
| if (encoder->newBg) { |
| subenc |= 0x2; // Background |
| encoder->server->convertPixels(buffer.data() + n, (char*)&encoder->bg, 1); |
| n += bpp; |
| // encoder->newBg = false; |
| } |
| |
| if (encoder->newFg) { |
| subenc |= 0x4; // Foreground |
| encoder->server->convertPixels(buffer.data() + n, (char*)&encoder->fg, 1); |
| n += bpp; |
| // encoder->newFg = false; |
| } |
| buffer[n] = numRects; |
| n += sizeof(numRects); |
| |
| socket->write(buffer.data() + padding, n - padding); |
| socket->write((char*)rects, numRects * sizeof(Rect)); |
| } |
| |
| template <class SRC> |
| void QRfbDualColorHextile<SRC>::next() |
| { |
| for (int r = numRects - 1; r >= 0; --r) { |
| if (recty(r) == lasty()) |
| continue; |
| if (recty(r) < lasty() - 1) // only search previous scanline |
| break; |
| if (rectx(r) == lastx() && width(r) == width(numRects)) { |
| ++rects[r].wh; |
| return; |
| } |
| } |
| ++numRects; |
| } |
| |
| template <class SRC> |
| inline void QRfbMultiColorHextile<SRC>::setColor(SRC color) |
| { |
| encoder->server->convertPixels(reinterpret_cast<char*>(rect(numRects)), |
| (const char*)&color, 1); |
| } |
| |
| template <class SRC> |
| inline bool QRfbMultiColorHextile<SRC>::beginRect() |
| { |
| if ((rects.size() + bpp + 2) > maxRectsSize) |
| return false; |
| rects.resize(rects.size() + bpp + 2); |
| return true; |
| } |
| |
| template <class SRC> |
| inline void QRfbMultiColorHextile<SRC>::endRect() |
| { |
| setHeight(numRects, 1); |
| ++numRects; |
| } |
| |
| template <class SRC> |
| bool QRfbMultiColorHextile<SRC>::read(const uchar *data, |
| int width, int height, int stride) |
| { |
| const SRC *ptr = reinterpret_cast<const SRC*>(data); |
| const int linestep = (stride / sizeof(SRC)) - width; |
| |
| bpp = encoder->server->clientBytesPerPixel(); |
| |
| if (encoder->newBg) |
| encoder->bg = ptr[0]; |
| |
| const SRC bg = encoder->bg; |
| SRC color = bg; |
| bool inRect = false; |
| |
| numRects = 0; |
| rects.clear(); |
| |
| for (int y = 0; y < height; ++y) { |
| for (int x = 0; x < width; ++x) { |
| if (inRect && *ptr != color) { // end rect |
| setWidth(numRects, x - rectx(numRects)); |
| endRect(); |
| inRect = false; |
| } |
| |
| if (!inRect && *ptr != bg) { // begin rect |
| if (!beginRect()) |
| return false; |
| inRect = true; |
| color = *ptr; |
| setColor(color); |
| setX(numRects, x); |
| setY(numRects, y); |
| } |
| ++ptr; |
| } |
| if (inRect) { // end rect |
| setWidth(numRects, width - rectx(numRects)); |
| endRect(); |
| inRect = false; |
| } |
| ptr += linestep; |
| } |
| |
| return true; |
| } |
| |
| template <class SRC> |
| void QRfbMultiColorHextile<SRC>::write(QTcpSocket *socket) const |
| { |
| const int padding = 3; |
| QVarLengthArray<quint8> buffer(bpp + padding + sizeof(quint8) + sizeof(numRects)); |
| |
| quint8 &subenc = buffer[padding]; |
| int n = padding + sizeof(quint8); |
| |
| subenc = 8 | 16; // AnySubrects | SubrectsColoured |
| |
| if (encoder->newBg) { |
| subenc |= 0x2; // Background |
| encoder->server->convertPixels(reinterpret_cast<char*>(buffer.data() + n), |
| reinterpret_cast<const char*>(&encoder->bg), |
| 1); |
| n += bpp; |
| // encoder->newBg = false; |
| } |
| |
| buffer[n] = numRects; |
| n += sizeof(numRects); |
| |
| socket->write(reinterpret_cast<const char*>(buffer.data() + padding), |
| n - padding); |
| socket->write(reinterpret_cast<const char*>(rects.constData()), |
| rects.size()); |
| } |
| |
| 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 18: |
| return (pixelFormat.redBits == 6 |
| && pixelFormat.greenBits == 6 |
| && pixelFormat.blueBits == 6); |
| case 16: |
| return (pixelFormat.redBits == 5 |
| && pixelFormat.greenBits == 6 |
| && pixelFormat.blueBits == 5); |
| case 15: |
| return (pixelFormat.redBits == 5 |
| && pixelFormat.greenBits == 5 |
| && pixelFormat.blueBits == 5); |
| case 12: |
| return (pixelFormat.redBits == 4 |
| && pixelFormat.greenBits == 4 |
| && pixelFormat.blueBits == 4); |
| } |
| return true; |
| } |
| |
| // count: number of pixels |
| void QVNCServer::convertPixels(char *dst, const char *src, int count) const |
| { |
| const int screendepth = qvnc_screen->depth(); |
| const bool isBgr = qvnc_screen->pixelType() == QScreen::BGRPixel; |
| |
| // 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<const quint32*>(src); |
| quint32 *dst32 = reinterpret_cast<quint32*>(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<const quint16*>(src); |
| *dst32 = qt_conv16ToRgb(src16[count - 1]); |
| } |
| return; |
| #endif |
| } |
| } |
| |
| const int bytesPerPixel = (pixelFormat.bitsPerPixel + 7) / 8; |
| |
| // nibble = 0; |
| |
| for (int i = 0; i < count; ++i) { |
| int r, g, b; |
| |
| switch (screendepth) { |
| #if 0 |
| case 4: { |
| if (!nibble) { |
| r = ((*src) & 0x0f) << 4; |
| } else { |
| r = (*src) & 0xf0; |
| src++; |
| } |
| nibble = !nibble; |
| g = b = r; |
| break; |
| } |
| #endif |
| case 8: { |
| QRgb rgb = qvnc_screen->clut()[int(*src)]; |
| r = qRed(rgb); |
| g = qGreen(rgb); |
| b = qBlue(rgb); |
| src++; |
| break; |
| } |
| #ifdef QT_QWS_DEPTH_12 |
| case 12: { |
| quint32 p = quint32(*reinterpret_cast<const qrgb444*>(src)); |
| r = qRed(p); |
| g = qGreen(p); |
| b = qBlue(p); |
| src += sizeof(qrgb444); |
| break; |
| } |
| #endif |
| #ifdef QT_QWS_DEPTH_15 |
| case 15: { |
| quint32 p = quint32(*reinterpret_cast<const qrgb555*>(src)); |
| r = qRed(p); |
| g = qGreen(p); |
| b = qBlue(p); |
| src += sizeof(qrgb555); |
| break; |
| } |
| #endif |
| case 16: { |
| quint16 p = *reinterpret_cast<const quint16*>(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; |
| } |
| #ifdef QT_QWS_DEPTH_18 |
| case 18: { |
| quint32 p = quint32(*reinterpret_cast<const qrgb666*>(src)); |
| r = qRed(p); |
| g = qGreen(p); |
| b = qBlue(p); |
| src += sizeof(qrgb666); |
| break; |
| } |
| #endif |
| #ifdef QT_QWS_DEPTH_24 |
| case 24: { |
| quint32 p = quint32(*reinterpret_cast<const qrgb888*>(src)); |
| r = qRed(p); |
| g = qGreen(p); |
| b = qBlue(p); |
| src += sizeof(qrgb888); |
| break; |
| } |
| #endif |
| case 32: { |
| quint32 p = *reinterpret_cast<const quint32*>(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 ^ isBgr) |
| #else |
| if (isBgr) |
| #endif |
| qSwap(r, b); |
| |
| 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); // XXX: do a simple for-loop instead? |
| 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); // XXX: simple for-loop instead? |
| dst += bytesPerPixel; |
| } |
| } |
| |
| #ifndef QT_NO_QWS_CURSOR |
| static void blendCursor(QImage &image, const QRect &imageRect) |
| { |
| const QRect cursorRect = qt_screencursor->boundingRect(); |
| const QRect intersection = (cursorRect & imageRect); |
| const QRect destRect = intersection.translated(-imageRect.topLeft()); |
| const QRect srcRect = intersection.translated(-cursorRect.topLeft()); |
| |
| QPainter painter(&image); |
| painter.drawImage(destRect, qt_screencursor->image(), srcRect); |
| painter.end(); |
| } |
| #endif // QT_NO_QWS_CURSOR |
| |
| QVNCDirtyMap::QVNCDirtyMap(QScreen *s) |
| : bytesPerPixel(0), numDirty(0), screen(s) |
| { |
| bytesPerPixel = (screen->depth() + 7) / 8; |
| bufferWidth = screen->deviceWidth(); |
| bufferHeight = screen->deviceHeight(); |
| bufferStride = bufferWidth * bytesPerPixel; |
| buffer = new uchar[bufferHeight * bufferStride]; |
| |
| mapWidth = (bufferWidth + MAP_TILE_SIZE - 1) / MAP_TILE_SIZE; |
| mapHeight = (bufferHeight + MAP_TILE_SIZE - 1) / MAP_TILE_SIZE; |
| numTiles = mapWidth * mapHeight; |
| map = new uchar[numTiles]; |
| } |
| |
| QVNCDirtyMap::~QVNCDirtyMap() |
| { |
| delete[] map; |
| delete[] buffer; |
| } |
| |
| void QVNCDirtyMap::reset() |
| { |
| memset(map, 1, numTiles); |
| memset(buffer, 0, bufferHeight * bufferStride); |
| numDirty = numTiles; |
| } |
| |
| inline bool QVNCDirtyMap::dirty(int x, int y) const |
| { |
| return map[y * mapWidth + x]; |
| } |
| |
| inline void QVNCDirtyMap::setClean(int x, int y) |
| { |
| map[y * mapWidth + x] = 0; |
| --numDirty; |
| } |
| |
| template <class T> |
| void QVNCDirtyMapOptimized<T>::setDirty(int tileX, int tileY, bool force) |
| { |
| static bool alwaysForce = qgetenv("QT_VNC_NO_COMPAREBUFFER").toInt(); |
| if (alwaysForce) |
| force = true; |
| |
| bool changed = false; |
| |
| if (!force) { |
| const int lstep = screen->linestep(); |
| const int startX = tileX * MAP_TILE_SIZE; |
| const int startY = tileY * MAP_TILE_SIZE; |
| const uchar *scrn = screen->base() |
| + startY * lstep + startX * bytesPerPixel; |
| uchar *old = buffer + startY * bufferStride + startX * sizeof(T); |
| |
| const int tileHeight = (startY + MAP_TILE_SIZE > bufferHeight ? |
| bufferHeight - startY : MAP_TILE_SIZE); |
| const int tileWidth = (startX + MAP_TILE_SIZE > bufferWidth ? |
| bufferWidth - startX : MAP_TILE_SIZE); |
| const bool doInlines = (tileWidth == MAP_TILE_SIZE); |
| |
| int y = tileHeight; |
| |
| if (doInlines) { // hw: memcmp/memcpy is inlined when using constants |
| while (y) { |
| if (memcmp(old, scrn, sizeof(T) * MAP_TILE_SIZE)) { |
| changed = true; |
| break; |
| } |
| scrn += lstep; |
| old += bufferStride; |
| --y; |
| } |
| |
| while (y) { |
| memcpy(old, scrn, sizeof(T) * MAP_TILE_SIZE); |
| scrn += lstep; |
| old += bufferStride; |
| --y; |
| } |
| } else { |
| while (y) { |
| if (memcmp(old, scrn, sizeof(T) * tileWidth)) { |
| changed = true; |
| break; |
| } |
| scrn += lstep; |
| old += bufferStride; |
| --y; |
| } |
| |
| while (y) { |
| memcpy(old, scrn, sizeof(T) * tileWidth); |
| scrn += lstep; |
| old += bufferStride; |
| --y; |
| } |
| } |
| } |
| |
| const int mapIndex = tileY * mapWidth + tileX; |
| if ((force || changed) && !map[mapIndex]) { |
| map[mapIndex] = 1; |
| ++numDirty; |
| } |
| } |
| |
| template <class SRC> |
| QRfbHextileEncoder<SRC>::QRfbHextileEncoder(QVNCServer *s) |
| : QRfbEncoder(s), |
| singleColorHextile(this), dualColorHextile(this), multiColorHextile(this) |
| { |
| } |
| |
| /* |
| \internal |
| Send dirty rects using hextile encoding. |
| */ |
| template <class SRC> |
| void QRfbHextileEncoder<SRC>::write() |
| { |
| QWSDisplay::grab(true); |
| |
| QVNCDirtyMap *map = server->dirtyMap(); |
| QTcpSocket *socket = server->clientSocket(); |
| |
| const quint32 encoding = htonl(5); // hextile encoding |
| const int bytesPerPixel = server->clientBytesPerPixel(); |
| |
| { |
| const char tmp[2] = { 0, 0 }; // msg type, padding |
| socket->write(tmp, sizeof(tmp)); |
| } |
| { |
| const quint16 count = htons(map->numDirty); |
| socket->write((char *)&count, sizeof(count)); |
| } |
| |
| if (map->numDirty <= 0) { |
| QWSDisplay::ungrab(); |
| return; |
| } |
| |
| newBg = true; |
| newFg = true; |
| |
| const QImage screenImage = server->screenImage(); |
| QRfbRect rect(0, 0, MAP_TILE_SIZE, MAP_TILE_SIZE); |
| |
| for (int y = 0; y < map->mapHeight; ++y) { |
| if (rect.y + MAP_TILE_SIZE > server->screen()->height()) |
| rect.h = server->screen()->height() - rect.y; |
| rect.w = MAP_TILE_SIZE; |
| for (int x = 0; x < map->mapWidth; ++x) { |
| if (!map->dirty(x, y)) |
| continue; |
| map->setClean(x, y); |
| |
| rect.x = x * MAP_TILE_SIZE; |
| if (rect.x + MAP_TILE_SIZE > server->screen()->deviceWidth()) |
| rect.w = server->screen()->deviceWidth() - rect.x; |
| rect.write(socket); |
| |
| socket->write((char *)&encoding, sizeof(encoding)); |
| |
| const uchar *screendata = screenImage.scanLine(rect.y) |
| + rect.x * screenImage.depth() / 8; |
| int linestep = screenImage.bytesPerLine(); |
| |
| #ifndef QT_NO_QWS_CURSOR |
| // hardware cursors must be blended with the screen memory |
| const bool doBlendCursor = qt_screencursor |
| && !server->hasClientCursor() |
| && qt_screencursor->isAccelerated(); |
| QImage tileImage; |
| if (doBlendCursor) { |
| const QRect tileRect(rect.x, rect.y, rect.w, rect.h); |
| const QRect cursorRect = qt_screencursor->boundingRect() |
| .translated(-server->screen()->offset()); |
| if (tileRect.intersects(cursorRect)) { |
| tileImage = screenImage.copy(tileRect); |
| blendCursor(tileImage, |
| tileRect.translated(server->screen()->offset())); |
| screendata = tileImage.bits(); |
| linestep = tileImage.bytesPerLine(); |
| } |
| } |
| #endif // QT_NO_QWS_CURSOR |
| |
| if (singleColorHextile.read(screendata, rect.w, rect.h, linestep)) { |
| singleColorHextile.write(socket); |
| } else if (dualColorHextile.read(screendata, rect.w, rect.h, linestep)) { |
| dualColorHextile.write(socket); |
| } else if (multiColorHextile.read(screendata, rect.w, rect.h, linestep)) { |
| multiColorHextile.write(socket); |
| } else if (server->doPixelConversion()) { |
| const int bufferSize = rect.w * rect.h * bytesPerPixel + 1; |
| const int padding = sizeof(quint32) - sizeof(char); |
| buffer.resize(bufferSize + padding); |
| |
| buffer[padding] = 1; // Raw subencoding |
| |
| // convert pixels |
| char *b = buffer.data() + padding + 1; |
| const int bstep = rect.w * bytesPerPixel; |
| for (int i = 0; i < rect.h; ++i) { |
| server->convertPixels(b, (const char*)screendata, rect.w); |
| screendata += linestep; |
| b += bstep; |
| } |
| socket->write(buffer.constData() + padding, bufferSize); |
| } else { |
| quint8 subenc = 1; // Raw subencoding |
| socket->write((char *)&subenc, 1); |
| |
| // send pixels |
| for (int i = 0; i < rect.h; ++i) { |
| socket->write((const char*)screendata, |
| rect.w * bytesPerPixel); |
| screendata += linestep; |
| } |
| } |
| } |
| if (socket->state() == QAbstractSocket::UnconnectedState) |
| break; |
| rect.y += MAP_TILE_SIZE; |
| } |
| socket->flush(); |
| Q_ASSERT(map->numDirty == 0); |
| |
| QWSDisplay::ungrab(); |
| } |
| |
| void QRfbRawEncoder::write() |
| { |
| QWSDisplay::grab(false); |
| |
| QVNCDirtyMap *map = server->dirtyMap(); |
| QTcpSocket *socket = server->clientSocket(); |
| |
| const int bytesPerPixel = server->clientBytesPerPixel(); |
| |
| // create a region from the dirty rects and send the region's merged rects. |
| QRegion rgn; |
| if (map) { |
| for (int y = 0; y < map->mapHeight; ++y) { |
| for (int x = 0; x < map->mapWidth; ++x) { |
| if (!map->dirty(x, y)) |
| continue; |
| rgn += QRect(x * MAP_TILE_SIZE, y * MAP_TILE_SIZE, |
| MAP_TILE_SIZE, MAP_TILE_SIZE); |
| map->setClean(x, y); |
| } |
| } |
| |
| rgn &= QRect(0, 0, server->screen()->deviceWidth(), |
| server->screen()->deviceHeight()); |
| } |
| const QVector<QRect> rects = rgn.rects(); |
| |
| { |
| const char tmp[2] = { 0, 0 }; // msg type, padding |
| socket->write(tmp, sizeof(tmp)); |
| } |
| |
| { |
| const quint16 count = htons(rects.size()); |
| socket->write((char *)&count, sizeof(count)); |
| } |
| |
| if (rects.size() <= 0) { |
| QWSDisplay::ungrab(); |
| return; |
| } |
| |
| const QImage screenImage = server->screenImage(); |
| |
| for (int i = 0; i < rects.size(); ++i) { |
| const QRect tileRect = rects.at(i); |
| const QRfbRect rect(tileRect.x(), tileRect.y(), |
| tileRect.width(), tileRect.height()); |
| rect.write(socket); |
| |
| const quint32 encoding = htonl(0); // raw encoding |
| socket->write((char *)&encoding, sizeof(encoding)); |
| |
| int linestep = screenImage.bytesPerLine(); |
| const uchar *screendata = screenImage.scanLine(rect.y) |
| + rect.x * screenImage.depth() / 8; |
| |
| #ifndef QT_NO_QWS_CURSOR |
| // hardware cursors must be blended with the screen memory |
| const bool doBlendCursor = qt_screencursor |
| && !server->hasClientCursor() |
| && qt_screencursor->isAccelerated(); |
| QImage tileImage; |
| if (doBlendCursor) { |
| const QRect cursorRect = qt_screencursor->boundingRect() |
| .translated(-server->screen()->offset()); |
| if (tileRect.intersects(cursorRect)) { |
| tileImage = screenImage.copy(tileRect); |
| blendCursor(tileImage, |
| tileRect.translated(server->screen()->offset())); |
| screendata = tileImage.bits(); |
| linestep = tileImage.bytesPerLine(); |
| } |
| } |
| #endif // QT_NO_QWS_CURSOR |
| |
| if (server->doPixelConversion()) { |
| const int bufferSize = rect.w * rect.h * bytesPerPixel; |
| if (bufferSize > buffer.size()) |
| buffer.resize(bufferSize); |
| |
| // convert pixels |
| 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); |
| screendata += linestep; |
| b += bstep; |
| } |
| socket->write(buffer.constData(), bufferSize); |
| } else { |
| for (int i = 0; i < rect.h; ++i) { |
| socket->write((const char*)screendata, rect.w * bytesPerPixel); |
| screendata += linestep; |
| } |
| } |
| if (socket->state() == QAbstractSocket::UnconnectedState) |
| break; |
| } |
| socket->flush(); |
| |
| QWSDisplay::ungrab(); |
| } |
| |
| inline QImage QVNCServer::screenImage() const |
| { |
| return QImage(qvnc_screen->base(), qvnc_screen->deviceWidth(), |
| qvnc_screen->deviceHeight(), qvnc_screen->linestep(), |
| qvnc_screen->pixelFormat()); |
| } |
| |
| void QVNCServer::checkUpdate() |
| { |
| if (!wantUpdate) |
| return; |
| |
| if (dirtyCursor) { |
| #ifndef QT_NO_QWS_CURSOR |
| Q_ASSERT(qvnc_cursor); |
| qvnc_cursor->write(); |
| #endif |
| dirtyCursor = false; |
| wantUpdate = false; |
| return; |
| } |
| |
| if (dirtyMap()->numDirty > 0) { |
| if (encoder) |
| encoder->write(); |
| wantUpdate = false; |
| } |
| } |
| |
| void QVNCServer::discardClient() |
| { |
| timer->stop(); |
| state = Unconnected; |
| delete encoder; |
| encoder = 0; |
| #ifndef QT_NO_QWS_CURSOR |
| delete qvnc_cursor; |
| qvnc_cursor = 0; |
| #endif |
| if (!qvnc_screen->screen() && !qvnc_screen->d_ptr->noDisablePainting && QWSServer::instance()) |
| QWSServer::instance()->enablePainting(false); |
| } |
| |
| |
| //=========================================================================== |
| |
| /*! |
| \class QVNCScreen |
| \internal |
| \ingroup qws |
| |
| \brief The QVNCScreen class implements a screen driver for VNC |
| servers. |
| |
| Note that this class is only available in \l{Qt for Embedded Linux}. |
| Custom screen drivers can be added by subclassing the QScreen |
| class, using the QScreenDriverFactory class to dynamically load |
| the driver into the application. |
| |
| The VNC protocol allows you to view and interact with the |
| computer's display from anywhere on the network. See the |
| \l{The VNC Protocol and Qt for Embedded Linux}{VNC protocol} |
| documentation for more details. |
| |
| The default implementation of QVNCScreen inherits QLinuxFbScreen, |
| but any QScreen subclass, or QScreen itself, can serve as its base |
| class. This is easily achieved by manipulating the \c |
| VNCSCREEN_BASE definition in the header file. |
| |
| \sa QScreen, {Running Applications} |
| */ |
| |
| /*! |
| \fn QVNCScreen::QVNCScreen(int displayId) |
| |
| Constructs a QVNCScreen object. The \a displayId argument |
| identifies the Qt for Embedded Linux server to connect to. |
| */ |
| QVNCScreen::QVNCScreen(int display_id) |
| : QProxyScreen(display_id, VNCClass) |
| { |
| d_ptr = new QVNCScreenPrivate(this); |
| } |
| |
| /*! |
| Destroys this QVNCScreen object. |
| */ |
| QVNCScreen::~QVNCScreen() |
| { |
| delete d_ptr; |
| } |
| |
| /*! |
| \reimp |
| */ |
| void QVNCScreen::setDirty(const QRect &rect) |
| { |
| d_ptr->setDirty(rect); |
| } |
| |
| void QVNCScreenPrivate::setDirty(const QRect& rect, bool force) |
| { |
| if (rect.isEmpty()) |
| return; |
| |
| if (q_ptr->screen()) |
| q_ptr->screen()->setDirty(rect); |
| |
| if (!vncServer || !vncServer->isConnected()) |
| return; |
| |
| const QRect r = rect.translated(-q_ptr->offset()); |
| const int x1 = r.x() / MAP_TILE_SIZE; |
| int y = r.y() / MAP_TILE_SIZE; |
| for (; (y <= r.bottom() / MAP_TILE_SIZE) && y < dirty->mapHeight; y++) |
| for (int x = x1; (x <= r.right() / MAP_TILE_SIZE) && x < dirty->mapWidth; x++) |
| dirty->setDirty(x, y, force); |
| |
| vncServer->setDirty(); |
| } |
| |
| static int getDisplayId(const QString &spec) |
| { |
| QRegExp regexp(QLatin1String(":(\\d+)\\b")); |
| if (regexp.lastIndexIn(spec) != -1) { |
| const QString capture = regexp.cap(1); |
| return capture.toInt(); |
| } |
| return 0; |
| } |
| |
| /*! |
| \reimp |
| */ |
| bool QVNCScreen::connect(const QString &displaySpec) |
| { |
| QString dspec = displaySpec; |
| if (dspec.startsWith(QLatin1String("vnc:"), Qt::CaseInsensitive)) |
| dspec = dspec.mid(QString::fromLatin1("vnc:").size()); |
| else if (dspec.compare(QLatin1String("vnc"), Qt::CaseInsensitive) == 0) |
| dspec = QString(); |
| |
| const QString displayIdSpec = QString::fromLatin1(" :%1").arg(displayId); |
| if (dspec.endsWith(displayIdSpec)) |
| dspec = dspec.left(dspec.size() - displayIdSpec.size()); |
| |
| QStringList args = dspec.split(QLatin1Char(':'), |
| QString::SkipEmptyParts); |
| QRegExp refreshRegexp(QLatin1String("^refreshrate=(\\d+)$")); |
| int index = args.indexOf(refreshRegexp); |
| if (index >= 0) { |
| d_ptr->refreshRate = refreshRegexp.cap(1).toInt(); |
| args.removeAt(index); |
| dspec = args.join(QLatin1String(":")); |
| } |
| |
| QString driver = dspec; |
| int colon = driver.indexOf(QLatin1Char(':')); |
| if (colon >= 0) |
| driver.truncate(colon); |
| |
| if (QScreenDriverFactory::keys().contains(driver, Qt::CaseInsensitive)) { |
| const int id = getDisplayId(dspec); |
| QScreen *s = qt_get_screen(id, dspec.toLatin1().constData()); |
| if (s->pixelFormat() == QImage::Format_Indexed8 |
| || s->pixelFormat() == QImage::Format_Invalid && s->depth() == 8) |
| qFatal("QVNCScreen: unsupported screen format"); |
| setScreen(s); |
| } else { // create virtual screen |
| #if Q_BYTE_ORDER == Q_BIG_ENDIAN |
| QScreen::setFrameBufferLittleEndian(false); |
| #endif |
| |
| d = qgetenv("QWS_DEPTH").toInt(); |
| if (!d) |
| d = 16; |
| |
| QByteArray str = qgetenv("QWS_SIZE"); |
| if(!str.isEmpty()) { |
| sscanf(str.constData(), "%dx%d", &w, &h); |
| dw = w; |
| dh = h; |
| } else { |
| dw = w = 640; |
| dh = h = 480; |
| } |
| |
| const QStringList args = displaySpec.split(QLatin1Char(':'), |
| QString::SkipEmptyParts); |
| |
| if (args.contains(QLatin1String("paintonscreen"), Qt::CaseInsensitive)) |
| d_ptr->doOnScreenSurface = true; |
| |
| QRegExp depthRegexp(QLatin1String("^depth=(\\d+)$")); |
| if (args.indexOf(depthRegexp) != -1) |
| d = depthRegexp.cap(1).toInt(); |
| |
| QRegExp sizeRegexp(QLatin1String("^size=(\\d+)x(\\d+)$")); |
| if (args.indexOf(sizeRegexp) != -1) { |
| dw = w = sizeRegexp.cap(1).toInt(); |
| dh = h = sizeRegexp.cap(2).toInt(); |
| } |
| |
| // Handle display physical size spec. |
| QRegExp mmWidthRegexp(QLatin1String("^mmWidth=?(\\d+)$")); |
| if (args.indexOf(mmWidthRegexp) != -1) { |
| const int mmWidth = mmWidthRegexp.cap(1).toInt(); |
| if (mmWidth > 0) |
| d_ptr->dpiX = dw * 25.4 / mmWidth; |
| } |
| QRegExp mmHeightRegexp(QLatin1String("^mmHeight=?(\\d+)$")); |
| if (args.indexOf(mmHeightRegexp) != -1) { |
| const int mmHeight = mmHeightRegexp.cap(1).toInt(); |
| if (mmHeight > 0) |
| d_ptr->dpiY = dh * 25.4 / mmHeight; |
| } |
| QRegExp dpiRegexp(QLatin1String("^dpi=(\\d+)(?:,(\\d+))?$")); |
| if (args.indexOf(dpiRegexp) != -1) { |
| const qreal dpiX = dpiRegexp.cap(1).toFloat(); |
| const qreal dpiY = dpiRegexp.cap(2).toFloat(); |
| if (dpiX > 0) |
| d_ptr->dpiX = dpiX; |
| d_ptr->dpiY = (dpiY > 0 ? dpiY : dpiX); |
| } |
| |
| if (args.contains(QLatin1String("noDisablePainting"))) |
| d_ptr->noDisablePainting = true; |
| |
| QWSServer::setDefaultMouse("None"); |
| QWSServer::setDefaultKeyboard("None"); |
| |
| d_ptr->configure(); |
| } |
| |
| // XXX |
| qt_screen = this; |
| |
| return true; |
| } |
| |
| /*! |
| \reimp |
| */ |
| void QVNCScreen::disconnect() |
| { |
| QProxyScreen::disconnect(); |
| #if !defined(QT_NO_QWS_MULTIPROCESS) && !defined(QT_NO_SHAREDMEMORY) |
| d_ptr->shm.detach(); |
| #endif |
| } |
| |
| /*! |
| \reimp |
| */ |
| bool QVNCScreen::initDevice() |
| { |
| if (!QProxyScreen::screen() && d == 4) { |
| screencols = 16; |
| int val = 0; |
| for (int idx = 0; idx < 16; idx++, val += 17) { |
| screenclut[idx] = qRgb(val, val, val); |
| } |
| } |
| d_ptr->vncServer = new QVNCServer(this, displayId); |
| d_ptr->vncServer->setRefreshRate(d_ptr->refreshRate); |
| |
| switch (depth()) { |
| #ifdef QT_QWS_DEPTH_32 |
| case 32: |
| d_ptr->dirty = new QVNCDirtyMapOptimized<quint32>(this); |
| break; |
| #endif |
| #ifdef QT_QWS_DEPTH_24 |
| case 24: |
| d_ptr->dirty = new QVNCDirtyMapOptimized<qrgb888>(this); |
| break; |
| #endif |
| #ifdef QT_QWS_DEPTH_18 |
| case 18: |
| d_ptr->dirty = new QVNCDirtyMapOptimized<qrgb666>(this); |
| break; |
| #endif |
| #ifdef QT_QWS_DEPTH_16 |
| case 16: |
| d_ptr->dirty = new QVNCDirtyMapOptimized<quint16>(this); |
| break; |
| #endif |
| #ifdef QT_QWS_DEPTH_15 |
| case 15: |
| d_ptr->dirty = new QVNCDirtyMapOptimized<qrgb555>(this); |
| break; |
| #endif |
| #ifdef QT_QWS_DEPTH_12 |
| case 12: |
| d_ptr->dirty = new QVNCDirtyMapOptimized<qrgb444>(this); |
| break; |
| #endif |
| #ifdef QT_QWS_DEPTH_8 |
| case 8: |
| d_ptr->dirty = new QVNCDirtyMapOptimized<quint8>(this); |
| break; |
| #endif |
| default: |
| qWarning("QVNCScreen::initDevice: No support for screen depth %d", |
| depth()); |
| d_ptr->dirty = 0; |
| return false; |
| } |
| |
| |
| const bool ok = QProxyScreen::initDevice(); |
| #ifndef QT_NO_QWS_CURSOR |
| qt_screencursor = new QVNCCursor(this); |
| #endif |
| if (QProxyScreen::screen()) |
| return ok; |
| |
| // Disable painting if there is only 1 display and nothing is attached to the VNC server |
| if (!d_ptr->noDisablePainting) |
| QWSServer::instance()->enablePainting(false); |
| |
| return true; |
| } |
| |
| /*! |
| \reimp |
| */ |
| void QVNCScreen::shutdownDevice() |
| { |
| QProxyScreen::shutdownDevice(); |
| delete d_ptr->vncServer; |
| delete d_ptr->dirty; |
| } |
| |
| QT_END_NAMESPACE |
| |
| #endif // QT_NO_QWS_VNC |