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
parent
2204e9a7c4
commit
2cf3696d9f
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 ®ion)
|
||||
{
|
||||
m_dirtyRegion += region;
|
||||
if (m_state == Connected &&
|
||||
((m_server->dirtyMap()->numDirty > 0) || m_dirtyCursor)) {
|
||||
scheduleUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
void QVncClient::convertPixels(char *dst, const char *src, int count) const
|
||||
{
|
||||
const int screendepth = m_server->screen()->depth();
|
||||
|
||||
// cutoffs
|
||||
#if Q_BYTE_ORDER == Q_BIG_ENDIAN
|
||||
if (!m_swapBytes)
|
||||
#endif
|
||||
if (m_sameEndian) {
|
||||
if (screendepth == m_pixelFormat.bitsPerPixel) { // memcpy cutoffs
|
||||
|
||||
switch (screendepth) {
|
||||
case 32:
|
||||
memcpy(dst, src, count * sizeof(quint32));
|
||||
return;
|
||||
case 16:
|
||||
if (m_pixelFormat.redBits == 5
|
||||
&& m_pixelFormat.greenBits == 6
|
||||
&& m_pixelFormat.blueBits == 5)
|
||||
{
|
||||
memcpy(dst, src, count * sizeof(quint16));
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else if (screendepth == 16 && m_pixelFormat.bitsPerPixel == 32) {
|
||||
#if defined(__i386__) // Currently fails on ARM if dst is not 4 byte aligned
|
||||
const quint32 *src32 = reinterpret_cast<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
|
||||
|
|
@ -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 ®ion);
|
||||
void setDirtyCursor() { m_dirtyCursor = true; scheduleUpdate(); }
|
||||
QRegion dirtyRegion() const { return m_dirtyRegion; }
|
||||
inline bool isConnected() const { return m_state == Connected; }
|
||||
|
||||
inline int clientBytesPerPixel() const {
|
||||
return m_pixelFormat.bitsPerPixel / 8;
|
||||
}
|
||||
|
||||
void convertPixels(char *dst, const char *src, int count) const;
|
||||
inline bool doPixelConversion() const { return m_needConversion; }
|
||||
|
||||
signals:
|
||||
|
||||
private slots:
|
||||
void readClient();
|
||||
void discardClient();
|
||||
void checkUpdate();
|
||||
void scheduleUpdate();
|
||||
|
||||
protected:
|
||||
bool event(QEvent *event) override;
|
||||
|
||||
private:
|
||||
enum ClientState {
|
||||
Disconnected,
|
||||
Protocol,
|
||||
Authentication,
|
||||
Init,
|
||||
Connected
|
||||
};
|
||||
enum ProtocolVersion {
|
||||
V3_3,
|
||||
V3_7,
|
||||
V3_8
|
||||
};
|
||||
|
||||
void setPixelFormat();
|
||||
void setEncodings();
|
||||
void frameBufferUpdateRequest();
|
||||
void pointerEvent();
|
||||
void keyEvent();
|
||||
void clientCutText();
|
||||
bool pixelConversionNeeded() const;
|
||||
|
||||
QVncServer *m_server;
|
||||
QTcpSocket *m_clientSocket;
|
||||
QRfbEncoder *m_encoder;
|
||||
|
||||
// Client State
|
||||
ClientState m_state;
|
||||
quint8 m_msgType;
|
||||
bool m_handleMsg;
|
||||
QRfbPixelFormat m_pixelFormat;
|
||||
bool m_sameEndian;
|
||||
bool m_needConversion;
|
||||
int m_encodingsPending;
|
||||
int m_cutTextPending;
|
||||
uint m_supportCopyRect : 1;
|
||||
uint m_supportRRE : 1;
|
||||
uint m_supportCoRRE : 1;
|
||||
uint m_supportHextile : 1;
|
||||
uint m_supportZRLE : 1;
|
||||
uint m_supportCursor : 1;
|
||||
uint m_supportDesktopSize : 1;
|
||||
bool m_wantUpdate;
|
||||
Qt::KeyboardModifiers m_keymod;
|
||||
bool m_dirtyCursor;
|
||||
bool m_updatePending;
|
||||
#if Q_BYTE_ORDER == Q_BIG_ENDIAN
|
||||
bool m_swapBytes;
|
||||
#endif
|
||||
QRegion m_dirtyRegion;
|
||||
ProtocolVersion m_protocolVersion;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QVNCCLIENT_H
|
||||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue