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 <laszlo.agocs@qt.io>
bb10
Andy Nichols 2016-06-15 15:07:41 +02:00 committed by Andy Nichols
parent 2204e9a7c4
commit 2cf3696d9f
7 changed files with 951 additions and 729 deletions

View File

@ -38,18 +38,22 @@
****************************************************************************/
#include "qvnc_p.h"
#include "qvncscreen.h"
#include "qvncclient.h"
#include "QtNetwork/qtcpserver.h"
#include "QtNetwork/qtcpsocket.h"
#include <qpa/qwindowsysteminterface.h>
#include <QtGui/qguiapplication.h>
#include <qthread.h>
#include <QtGui/qguiapplication.h>
#include <QtGui/QWindow>
#ifdef Q_OS_WIN
#include <Winsock2.h>
#else
#include <arpa/inet.h>
#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()
void QVncServer::discardClient(QVncClient *client)
{
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;
clients.removeOne(client);
client->deleteLater();
if (clients.isEmpty()) {
qvnc_screen->disableClientCursor(client);
qvnc_screen->setPowerState(QPlatformScreen::PowerStateOff);
}
}
#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);
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
}
}
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<quint8>(this);
break;
case 16:
encoder = new QRfbHextileEncoder<quint16>(this);
break;
case 32:
encoder = new QRfbHextileEncoder<quint32>(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<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;
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<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;
}
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)
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

View File

@ -53,12 +53,15 @@
#include <QtCore/qvarlengtharray.h>
#include <qpa/qplatformcursor.h>
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<QVncClient *> 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<QVncClient*> clients;
QVncScreen *qvnc_screen;
QRfbEncoder *encoder;
};
QT_END_NAMESPACE
#endif

View File

@ -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 <QtNetwork/QTcpSocket>
#include <QtCore/QCoreApplication>
#include <qpa/qwindowsysteminterface.h>
#include <QtGui/qguiapplication.h>
#ifdef Q_OS_WIN
#include <Winsock2.h>
#else
#include <arpa/inet.h>
#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 &region)
{
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<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 = (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<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;
}
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 (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

View File

@ -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 <QObject>
#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 &region);
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

View File

@ -70,8 +70,6 @@ bool QVncScreen::initialize()
QFbScreen::initializeCompositor();
QT_VNC_DEBUG() << "QVncScreen::init" << geometry();
disableClientCursor();
switch (depth()) {
case 32:
dirty = new QVncDirtyMapOptimized<quint32>(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

View File

@ -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;

View File

@ -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