Commit 1902ff00 authored by Oleg `Kanedias` Chernovskiy's avatar Oleg `Kanedias` Chernovskiy Committed by Albert Astals Cid
Browse files

Add rendering of server-side cursor locally

This adds handling of cursor shape received from server.
This allows us to show same cursor locally.

If local cursor is requested/disabled, server is reconfigured with proper
encodings to enable/disable support for sending it.

If server does not support sending local cursor,
default Qt cursor is shown as a fallback.

Note: sometimes VNC server does not support changing from
sending local to showing remote cursor and this causes slight delay
due to reconnect.
parent 4b230205
......@@ -159,7 +159,6 @@ target_link_libraries(krdc
install(TARGETS krdc ${INSTALL_TARGETS_DEFAULT_ARGS})
install(FILES krdcui.rc DESTINATION ${KXMLGUI_INSTALL_DIR}/krdc)
install(PROGRAMS org.kde.krdc.desktop DESTINATION ${XDG_APPS_INSTALL_DIR})
install(FILES pointcursor.png pointcursormask.png DESTINATION ${DATA_INSTALL_DIR}/krdc/pics)
install(FILES org.kde.krdc.appdata.xml DESTINATION ${KDE_INSTALL_METAINFODIR})
feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES)
......@@ -31,6 +31,9 @@
#include <sys/socket.h>
#include <QMutexLocker>
#include <QTimer>
#include <QBitmap>
#include <QPixmap>
#include <QCursor>
//for detecting intel AMT KVM vnc server
static const QString INTEL_AMT_KVM_STRING = QLatin1String("Intel(r) AMT KVM");
......@@ -92,6 +95,40 @@ void VncClientThread::outputHandlerStatic(const char *format, ...)
va_end(args);
}
void VncClientThread::cursorShapeHandlerStatic(rfbClient *cl, int xhot, int yhot, int width, int height, int bpp) {
VncClientThread *t = (VncClientThread *) rfbClientGetClientData(cl, nullptr);
Q_ASSERT(t);
// get cursor shape from remote cursor field
// it's important to set stride for images, pictures in VNC are not always 32-bit aligned
QImage cursorImg;
switch (bpp) {
case 4:
cursorImg = QImage(cl->rcSource, width, height, bpp * width, QImage::Format_RGB32);
break;
case 2:
cursorImg = QImage(cl->rcSource, width, height, bpp * width, QImage::Format_RGB16);
break;
case 1:
cursorImg = QImage(cl->rcSource, width, height, bpp * width, QImage::Format_Indexed8);
cursorImg.setColorTable(t->m_colorTable);
break;
default:
qCWarning(KRDC) << "Unsupported bpp value for cursor shape:" << bpp;
return;
}
// get alpha channel
QImage alpha(cl->rcMask, width, height, 1 * width, QImage::Format_Indexed8);
alpha.setColorTable({qRgb(255, 255, 255), qRgb(0, 0, 0)});
// apply transparency mask
QPixmap cursorPixmap(QPixmap::fromImage(cursorImg));
cursorPixmap.setMask(QBitmap::fromImage(alpha));
emit t->gotCursor({cursorPixmap, xhot, yhot});
}
void VncClientThread::setClientColorDepth(rfbClient* cl, VncClientThread::ColorDepth cd)
{
switch(cd) {
......@@ -379,6 +416,23 @@ void VncClientThread::setPort(int port)
m_port = port;
}
void VncClientThread::setShowLocalCursor(bool show) {
QMutexLocker locker(&mutex);
m_showLocalCursor = show;
if (!cl) {
// no client yet, only store local value
return;
}
// from server point of view, "remote" cursor is the one local to the client
// so the meaning in AppData struct is inverted
cl->appData.useRemoteCursor = show;
// need to communicate this change to server or it won't stop painting cursor
m_eventQueue.enqueue(new ReconfigureEvent);
}
void VncClientThread::setQuality(RemoteView::Quality quality)
{
m_quality = quality;
......@@ -531,8 +585,11 @@ bool VncClientThread::clientCreate(bool reinitialising)
cl->GetCredential = credentialHandlerStatic;
cl->GotFrameBufferUpdate = updatefbStatic;
cl->GotXCutText = cuttextStatic;
cl->GotCursorShape = cursorShapeHandlerStatic;
rfbClientSetClientData(cl, nullptr, this);
cl->appData.useRemoteCursor = m_showLocalCursor;
cl->serverHost = strdup(m_host.toUtf8().constData());
cl->serverPort = m_port;
......@@ -627,6 +684,11 @@ ClientEvent::~ClientEvent()
{
}
void ReconfigureEvent::fire(rfbClient* cl)
{
SetFormatAndEncodings(cl);
}
void PointerClientEvent::fire(rfbClient* cl)
{
SendPointerEvent(cl, m_x, m_y, m_buttonMask);
......
......@@ -49,6 +49,12 @@ public:
virtual void fire(rfbClient*) = 0;
};
class ReconfigureEvent: public ClientEvent
{
public:
void fire(rfbClient*) override;
};
class KeyClientEvent : public ClientEvent
{
public:
......@@ -113,6 +119,7 @@ public:
void setPassword(const QString &password) {
m_password = password;
}
void setShowLocalCursor(bool show);
const QString password() const {
return m_password;
}
......@@ -130,6 +137,7 @@ public:
Q_SIGNALS:
void imageUpdated(int x, int y, int w, int h);
void gotCut(const QString &text);
void gotCursor(const QCursor &cursor);
void passwordRequest(bool includingUsername = false);
void outputErrorMessage(const QString &message);
......@@ -162,6 +170,7 @@ private:
static char *passwdHandlerStatic(rfbClient *cl);
static rfbCredential *credentialHandlerStatic(rfbClient *cl, int credentialType);
static void outputHandlerStatic(const char *format, ...);
static void cursorShapeHandlerStatic(rfbClient *cl, int xhot, int yhot, int width, int height, int bpp);
// Member functions corresponding to the above static methods.
rfbBool newclient();
......@@ -177,6 +186,7 @@ private:
QString m_password;
QString m_username;
int m_port;
bool m_showLocalCursor;
QMutex mutex;
RemoteView::Quality m_quality;
ColorDepth m_colorDepth;
......
......@@ -81,6 +81,7 @@ VncView::VncView(QWidget *parent, const QUrl &url, KConfigGroup configGroup)
connect(&vncThread, SIGNAL(gotCut(QString)), this, SLOT(setCut(QString)), Qt::BlockingQueuedConnection);
connect(&vncThread, SIGNAL(passwordRequest(bool)), this, SLOT(requestPassword(bool)), Qt::BlockingQueuedConnection);
connect(&vncThread, SIGNAL(outputErrorMessage(QString)), this, SLOT(outputErrorMessage(QString)));
connect(&vncThread, &VncClientThread::gotCursor, this, [this](QCursor cursor){ setCursor(cursor); });
m_clipboard = QApplication::clipboard();
connect(m_clipboard, SIGNAL(dataChanged()), this, SLOT(clipboardDataChanged()));
......@@ -508,7 +509,15 @@ void VncView::showLocalCursor(LocalCursorState state)
{
RemoteView::showLocalCursor(state);
setCursor(state == CursorOn ? localDefaultCursor() : Qt::BlankCursor);
if (state == CursorOn) {
// show local cursor, hide remote one
setCursor(localDefaultCursor());
vncThread.setShowLocalCursor(true);
} else {
// hide local cursor, show remote one
setCursor(Qt::BlankCursor);
vncThread.setShowLocalCursor(false);
}
}
void VncView::enableScaling(bool scale)
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment