aboutsummaryrefslogtreecommitdiffstats
path: root/common
diff options
context:
space:
mode:
authorPierre Ossman <ossman@cendio.se>2017-09-15 11:03:48 +0200
committerPierre Ossman <ossman@cendio.se>2017-09-15 11:03:48 +0200
commit24e83052d28d464e7f3f12aa800736bcdd16874a (patch)
treea09f3373566e759131255ac5f8648f90c37d6544 /common
parent71ca8d546baaf13cca282c840b6521a98078fb42 (diff)
parent0c15866f0c86402016e55eeb7c044eb2e6cd95f5 (diff)
downloadtigervnc-24e83052d28d464e7f3f12aa800736bcdd16874a.tar.gz
tigervnc-24e83052d28d464e7f3f12aa800736bcdd16874a.zip
Merge branch 'qemukbd-merge' of https://github.com/CendioOssman/tigervnc
Diffstat (limited to 'common')
-rw-r--r--common/rfb/CMsgHandler.cxx10
-rw-r--r--common/rfb/CMsgHandler.h3
-rw-r--r--common/rfb/CMsgReader.cxx14
-rw-r--r--common/rfb/CMsgReader.h1
-rw-r--r--common/rfb/CMsgWriter.cxx31
-rw-r--r--common/rfb/CMsgWriter.h4
-rw-r--r--common/rfb/ConnParams.cxx16
-rw-r--r--common/rfb/ConnParams.h6
-rw-r--r--common/rfb/InputHandler.h2
-rw-r--r--common/rfb/SConnection.cxx5
-rw-r--r--common/rfb/SConnection.h1
-rw-r--r--common/rfb/SMsgHandler.cxx17
-rw-r--r--common/rfb/SMsgHandler.h11
-rw-r--r--common/rfb/SMsgReader.cxx29
-rw-r--r--common/rfb/SMsgReader.h3
-rw-r--r--common/rfb/SMsgWriter.cxx75
-rw-r--r--common/rfb/SMsgWriter.h11
-rw-r--r--common/rfb/VNCSConnectionST.cxx178
-rw-r--r--common/rfb/VNCSConnectionST.h9
-rw-r--r--common/rfb/VNCServer.h4
-rw-r--r--common/rfb/VNCServerST.cxx18
-rw-r--r--common/rfb/VNCServerST.h2
-rw-r--r--common/rfb/encodings.h2
-rw-r--r--common/rfb/ledStates.h30
-rw-r--r--common/rfb/msgTypes.h2
-rw-r--r--common/rfb/qemuTypes.h25
26 files changed, 471 insertions, 38 deletions
diff --git a/common/rfb/CMsgHandler.cxx b/common/rfb/CMsgHandler.cxx
index 11c979a3..b89bc184 100644
--- a/common/rfb/CMsgHandler.cxx
+++ b/common/rfb/CMsgHandler.cxx
@@ -75,6 +75,11 @@ void CMsgHandler::endOfContinuousUpdates()
cp.supportsContinuousUpdates = true;
}
+void CMsgHandler::supportsQEMUKeyEvent()
+{
+ cp.supportsQEMUKeyEvent = true;
+}
+
void CMsgHandler::framebufferUpdateStart()
{
}
@@ -82,3 +87,8 @@ void CMsgHandler::framebufferUpdateStart()
void CMsgHandler::framebufferUpdateEnd()
{
}
+
+void CMsgHandler::setLEDState(unsigned int state)
+{
+ cp.setLEDState(state);
+}
diff --git a/common/rfb/CMsgHandler.h b/common/rfb/CMsgHandler.h
index 993276ed..903ee156 100644
--- a/common/rfb/CMsgHandler.h
+++ b/common/rfb/CMsgHandler.h
@@ -55,6 +55,7 @@ namespace rfb {
virtual void setName(const char* name);
virtual void fence(rdr::U32 flags, unsigned len, const char data[]);
virtual void endOfContinuousUpdates();
+ virtual void supportsQEMUKeyEvent();
virtual void serverInit() = 0;
virtual void readAndDecodeRect(const Rect& r, int encoding,
@@ -69,6 +70,8 @@ namespace rfb {
virtual void bell() = 0;
virtual void serverCutText(const char* str, rdr::U32 len) = 0;
+ virtual void setLEDState(unsigned int state);
+
ConnParams cp;
};
}
diff --git a/common/rfb/CMsgReader.cxx b/common/rfb/CMsgReader.cxx
index 9abe3f24..eee6d277 100644
--- a/common/rfb/CMsgReader.cxx
+++ b/common/rfb/CMsgReader.cxx
@@ -109,6 +109,11 @@ void CMsgReader::readMsg()
case pseudoEncodingExtendedDesktopSize:
readExtendedDesktopSize(x, y, w, h);
break;
+ case pseudoEncodingLEDState:
+ readLEDState();
+ case pseudoEncodingQEMUKeyEvent:
+ handler->supportsQEMUKeyEvent();
+ break;
default:
readRect(Rect(x, y, x+w, y+h), encoding);
break;
@@ -382,3 +387,12 @@ void CMsgReader::readExtendedDesktopSize(int x, int y, int w, int h)
handler->setExtendedDesktopSize(x, y, w, h, layout);
}
+
+void CMsgReader::readLEDState()
+{
+ rdr::U8 state;
+
+ state = is->readU8();
+
+ handler->setLEDState(state);
+}
diff --git a/common/rfb/CMsgReader.h b/common/rfb/CMsgReader.h
index 7b52033f..99638276 100644
--- a/common/rfb/CMsgReader.h
+++ b/common/rfb/CMsgReader.h
@@ -65,6 +65,7 @@ namespace rfb {
void readSetCursorWithAlpha(int width, int height, const Point& hotspot);
void readSetDesktopName(int x, int y, int w, int h);
void readExtendedDesktopSize(int x, int y, int w, int h);
+ void readLEDState();
CMsgHandler* handler;
rdr::InStream* is;
diff --git a/common/rfb/CMsgWriter.cxx b/common/rfb/CMsgWriter.cxx
index fa784048..84a0d1f1 100644
--- a/common/rfb/CMsgWriter.cxx
+++ b/common/rfb/CMsgWriter.cxx
@@ -21,6 +21,7 @@
#include <rfb/msgTypes.h>
#include <rfb/fenceTypes.h>
#include <rfb/encodings.h>
+#include <rfb/qemuTypes.h>
#include <rfb/Exception.h>
#include <rfb/PixelFormat.h>
#include <rfb/Rect.h>
@@ -82,10 +83,13 @@ void CMsgWriter::writeSetEncodings(int preferredEncoding, bool useCopyRect)
encodings[nEncodings++] = pseudoEncodingExtendedDesktopSize;
if (cp->supportsDesktopRename)
encodings[nEncodings++] = pseudoEncodingDesktopName;
+ if (cp->supportsLEDState)
+ encodings[nEncodings++] = pseudoEncodingLEDState;
encodings[nEncodings++] = pseudoEncodingLastRect;
encodings[nEncodings++] = pseudoEncodingContinuousUpdates;
encodings[nEncodings++] = pseudoEncodingFence;
+ encodings[nEncodings++] = pseudoEncodingQEMUKeyEvent;
if (Decoder::supported(preferredEncoding)) {
encodings[nEncodings++] = preferredEncoding;
@@ -213,13 +217,26 @@ void CMsgWriter::writeFence(rdr::U32 flags, unsigned len, const char data[])
endMsg();
}
-void CMsgWriter::keyEvent(rdr::U32 key, bool down)
+void CMsgWriter::keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down)
{
- startMsg(msgTypeKeyEvent);
- os->writeU8(down);
- os->pad(2);
- os->writeU32(key);
- endMsg();
+ if (!cp->supportsQEMUKeyEvent || !keycode) {
+ /* This event isn't meaningful without a valid keysym */
+ if (!keysym)
+ return;
+
+ startMsg(msgTypeKeyEvent);
+ os->writeU8(down);
+ os->pad(2);
+ os->writeU32(keysym);
+ endMsg();
+ } else {
+ startMsg(msgTypeQEMUClientMessage);
+ os->writeU8(qemuExtendedKeyEvent);
+ os->writeU16(down);
+ os->writeU32(keysym);
+ os->writeU32(keycode);
+ endMsg();
+ }
}
@@ -239,7 +256,7 @@ void CMsgWriter::pointerEvent(const Point& pos, int buttonMask)
}
-void CMsgWriter::clientCutText(const char* str, rdr::U32 len)
+void CMsgWriter::clientCutText(const char* str, int len)
{
startMsg(msgTypeClientCutText);
os->pad(3);
diff --git a/common/rfb/CMsgWriter.h b/common/rfb/CMsgWriter.h
index 06ecbe7d..b1f01195 100644
--- a/common/rfb/CMsgWriter.h
+++ b/common/rfb/CMsgWriter.h
@@ -55,9 +55,9 @@ namespace rfb {
// InputHandler implementation
- virtual void keyEvent(rdr::U32 key, bool down);
+ virtual void keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down);
virtual void pointerEvent(const Point& pos, int buttonMask);
- virtual void clientCutText(const char* str, rdr::U32 len);
+ virtual void clientCutText(const char* str, int len);
protected:
void startMsg(int type);
diff --git a/common/rfb/ConnParams.cxx b/common/rfb/ConnParams.cxx
index 9ee1d9c0..23f02ed0 100644
--- a/common/rfb/ConnParams.cxx
+++ b/common/rfb/ConnParams.cxx
@@ -22,6 +22,7 @@
#include <rdr/OutStream.h>
#include <rfb/Exception.h>
#include <rfb/encodings.h>
+#include <rfb/ledStates.h>
#include <rfb/ConnParams.h>
#include <rfb/util.h>
@@ -34,10 +35,12 @@ ConnParams::ConnParams()
supportsLocalCursorWithAlpha(false),
supportsDesktopResize(false), supportsExtendedDesktopSize(false),
supportsDesktopRename(false), supportsLastRect(false),
+ supportsLEDState(false), supportsQEMUKeyEvent(false),
supportsSetDesktopSize(false), supportsFence(false),
supportsContinuousUpdates(false),
compressLevel(2), qualityLevel(-1), fineQualityLevel(-1),
- subsampling(subsampleUndefined), name_(0), verStrPos(0)
+ subsampling(subsampleUndefined), name_(0), verStrPos(0),
+ ledState_(ledUnknown)
{
setName("");
cursor_ = new Cursor(0, 0, Point(), NULL);
@@ -107,6 +110,7 @@ void ConnParams::setEncodings(int nEncodings, const rdr::S32* encodings)
supportsExtendedDesktopSize = false;
supportsLocalXCursor = false;
supportsLastRect = false;
+ supportsQEMUKeyEvent = false;
compressLevel = -1;
qualityLevel = -1;
fineQualityLevel = -1;
@@ -141,6 +145,11 @@ void ConnParams::setEncodings(int nEncodings, const rdr::S32* encodings)
case pseudoEncodingLastRect:
supportsLastRect = true;
break;
+ case pseudoEncodingLEDState:
+ supportsLEDState = true;
+ case pseudoEncodingQEMUKeyEvent:
+ supportsQEMUKeyEvent = true;
+ break;
case pseudoEncodingFence:
supportsFence = true;
break;
@@ -183,3 +192,8 @@ void ConnParams::setEncodings(int nEncodings, const rdr::S32* encodings)
encodings_.insert(encodings[i]);
}
}
+
+void ConnParams::setLEDState(unsigned int state)
+{
+ ledState_ = state;
+}
diff --git a/common/rfb/ConnParams.h b/common/rfb/ConnParams.h
index 5e538933..b3222936 100644
--- a/common/rfb/ConnParams.h
+++ b/common/rfb/ConnParams.h
@@ -84,6 +84,9 @@ namespace rfb {
void setEncodings(int nEncodings, const rdr::S32* encodings);
+ unsigned int ledState() { return ledState_; }
+ void setLEDState(unsigned int state);
+
bool useCopyRect;
bool supportsLocalCursor;
@@ -93,6 +96,8 @@ namespace rfb {
bool supportsExtendedDesktopSize;
bool supportsDesktopRename;
bool supportsLastRect;
+ bool supportsLEDState;
+ bool supportsQEMUKeyEvent;
bool supportsSetDesktopSize;
bool supportsFence;
@@ -111,6 +116,7 @@ namespace rfb {
std::set<rdr::S32> encodings_;
char verStr[13];
int verStrPos;
+ unsigned int ledState_;
};
}
#endif
diff --git a/common/rfb/InputHandler.h b/common/rfb/InputHandler.h
index b5e5e879..0344bc3f 100644
--- a/common/rfb/InputHandler.h
+++ b/common/rfb/InputHandler.h
@@ -31,7 +31,7 @@ namespace rfb {
class InputHandler {
public:
virtual ~InputHandler() {}
- virtual void keyEvent(rdr::U32 key, bool down) {}
+ virtual void keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down) {}
virtual void pointerEvent(const Point& pos, int buttonMask) {}
virtual void clientCutText(const char* str, int len) {}
};
diff --git a/common/rfb/SConnection.cxx b/common/rfb/SConnection.cxx
index 85cc6e82..c5c9038c 100644
--- a/common/rfb/SConnection.cxx
+++ b/common/rfb/SConnection.cxx
@@ -278,6 +278,11 @@ void SConnection::setEncodings(int nEncodings, const rdr::S32* encodings)
SMsgHandler::setEncodings(nEncodings, encodings);
}
+void SConnection::supportsQEMUKeyEvent()
+{
+ writer()->writeQEMUKeyEvent();
+}
+
void SConnection::versionReceived()
{
}
diff --git a/common/rfb/SConnection.h b/common/rfb/SConnection.h
index 63dc3146..bc435834 100644
--- a/common/rfb/SConnection.h
+++ b/common/rfb/SConnection.h
@@ -73,6 +73,7 @@ namespace rfb {
virtual void setEncodings(int nEncodings, const rdr::S32* encodings);
+ virtual void supportsQEMUKeyEvent();
// Methods to be overridden in a derived class
diff --git a/common/rfb/SMsgHandler.cxx b/common/rfb/SMsgHandler.cxx
index 388b21f8..c38458c3 100644
--- a/common/rfb/SMsgHandler.cxx
+++ b/common/rfb/SMsgHandler.cxx
@@ -41,10 +41,13 @@ void SMsgHandler::setPixelFormat(const PixelFormat& pf)
void SMsgHandler::setEncodings(int nEncodings, const rdr::S32* encodings)
{
- bool firstFence, firstContinuousUpdates;
+ bool firstFence, firstContinuousUpdates, firstLEDState,
+ firstQEMUKeyEvent;
firstFence = !cp.supportsFence;
firstContinuousUpdates = !cp.supportsContinuousUpdates;
+ firstLEDState = !cp.supportsLEDState;
+ firstQEMUKeyEvent = !cp.supportsQEMUKeyEvent;
cp.setEncodings(nEncodings, encodings);
@@ -54,6 +57,10 @@ void SMsgHandler::setEncodings(int nEncodings, const rdr::S32* encodings)
supportsFence();
if (cp.supportsContinuousUpdates && firstContinuousUpdates)
supportsContinuousUpdates();
+ if (cp.supportsLEDState && firstLEDState)
+ supportsLEDState();
+ if (cp.supportsQEMUKeyEvent && firstQEMUKeyEvent)
+ supportsQEMUKeyEvent();
}
void SMsgHandler::supportsLocalCursor()
@@ -68,6 +75,14 @@ void SMsgHandler::supportsContinuousUpdates()
{
}
+void SMsgHandler::supportsLEDState()
+{
+}
+
+void SMsgHandler::supportsQEMUKeyEvent()
+{
+}
+
void SMsgHandler::setDesktopSize(int fb_width, int fb_height,
const ScreenSet& layout)
{
diff --git a/common/rfb/SMsgHandler.h b/common/rfb/SMsgHandler.h
index 74509e0b..749f0560 100644
--- a/common/rfb/SMsgHandler.h
+++ b/common/rfb/SMsgHandler.h
@@ -74,6 +74,17 @@ namespace rfb {
// this point if it is supported.
virtual void supportsContinuousUpdates();
+ // supportsLEDState() is called the first time we detect that the
+ // client supports the LED state extension. A LEDState message
+ // should be sent back to the client to inform it of the current
+ // server state.
+ virtual void supportsLEDState();
+
+ // supportsQEMUKeyEvent() is called the first time we detect that the
+ // client wants the QEMU Extended Key Event extension. The default
+ // handler will send a pseudo-rect back, signalling server support.
+ virtual void supportsQEMUKeyEvent();
+
ConnParams cp;
};
}
diff --git a/common/rfb/SMsgReader.cxx b/common/rfb/SMsgReader.cxx
index 3c08fd6f..cb71ac84 100644
--- a/common/rfb/SMsgReader.cxx
+++ b/common/rfb/SMsgReader.cxx
@@ -19,6 +19,7 @@
#include <stdio.h>
#include <rdr/InStream.h>
#include <rfb/msgTypes.h>
+#include <rfb/qemuTypes.h>
#include <rfb/Exception.h>
#include <rfb/util.h>
#include <rfb/SMsgHandler.h>
@@ -78,6 +79,9 @@ void SMsgReader::readMsg()
case msgTypeClientCutText:
readClientCutText();
break;
+ case msgTypeQEMUClientMessage:
+ readQEMUMessage();
+ break;
default:
fprintf(stderr, "unknown message type %d\n", msgType);
throw Exception("unknown message type");
@@ -184,7 +188,7 @@ void SMsgReader::readKeyEvent()
bool down = is->readU8();
is->skip(2);
rdr::U32 key = is->readU32();
- handler->keyEvent(key, down);
+ handler->keyEvent(key, 0, down);
}
void SMsgReader::readPointerEvent()
@@ -214,3 +218,26 @@ void SMsgReader::readClientCutText()
handler->clientCutText(ca.buf, len);
}
+void SMsgReader::readQEMUMessage()
+{
+ int subType = is->readU8();
+ switch (subType) {
+ case qemuExtendedKeyEvent:
+ readQEMUKeyEvent();
+ break;
+ default:
+ throw Exception("unknown QEMU submessage type %d", subType);
+ }
+}
+
+void SMsgReader::readQEMUKeyEvent()
+{
+ bool down = is->readU16();
+ rdr::U32 keysym = is->readU32();
+ rdr::U32 keycode = is->readU32();
+ if (!keycode) {
+ vlog.error("Key event without keycode - ignoring");
+ return;
+ }
+ handler->keyEvent(keysym, keycode, down);
+}
diff --git a/common/rfb/SMsgReader.h b/common/rfb/SMsgReader.h
index 00cb3031..146b29f8 100644
--- a/common/rfb/SMsgReader.h
+++ b/common/rfb/SMsgReader.h
@@ -55,6 +55,9 @@ namespace rfb {
void readPointerEvent();
void readClientCutText();
+ void readQEMUMessage();
+ void readQEMUKeyEvent();
+
SMsgHandler* handler;
rdr::InStream* is;
};
diff --git a/common/rfb/SMsgWriter.cxx b/common/rfb/SMsgWriter.cxx
index bc3f4398..2d4998b3 100644
--- a/common/rfb/SMsgWriter.cxx
+++ b/common/rfb/SMsgWriter.cxx
@@ -27,6 +27,7 @@
#include <rfb/Encoder.h>
#include <rfb/SMsgWriter.h>
#include <rfb/LogWriter.h>
+#include <rfb/ledStates.h>
using namespace rfb;
@@ -37,7 +38,8 @@ SMsgWriter::SMsgWriter(ConnParams* cp_, rdr::OutStream* os_)
nRectsInUpdate(0), nRectsInHeader(0),
needSetDesktopSize(false), needExtendedDesktopSize(false),
needSetDesktopName(false), needSetCursor(false),
- needSetXCursor(false), needSetCursorWithAlpha(false)
+ needSetXCursor(false), needSetCursorWithAlpha(false),
+ needLEDState(false), needQEMUKeyEvent(false)
{
}
@@ -193,12 +195,38 @@ bool SMsgWriter::writeSetCursorWithAlpha()
return true;
}
+bool SMsgWriter::writeLEDState()
+{
+ if (!cp->supportsLEDState)
+ return false;
+ if (cp->ledState() == ledUnknown)
+ return false;
+
+ needLEDState = true;
+
+ return true;
+}
+
+bool SMsgWriter::writeQEMUKeyEvent()
+{
+ if (!cp->supportsQEMUKeyEvent)
+ return false;
+
+ needQEMUKeyEvent = true;
+
+ return true;
+}
+
bool SMsgWriter::needFakeUpdate()
{
if (needSetDesktopName)
return true;
if (needSetCursor || needSetXCursor || needSetCursorWithAlpha)
return true;
+ if (needLEDState)
+ return true;
+ if (needQEMUKeyEvent)
+ return true;
if (needNoDataUpdate())
return true;
@@ -247,6 +275,10 @@ void SMsgWriter::writeFramebufferUpdateStart(int nRects)
nRects++;
if (needSetCursorWithAlpha)
nRects++;
+ if (needLEDState)
+ nRects++;
+ if (needQEMUKeyEvent)
+ nRects++;
}
os->writeU16(nRects);
@@ -362,6 +394,16 @@ void SMsgWriter::writePseudoRects()
writeSetDesktopNameRect(cp->name());
needSetDesktopName = false;
}
+
+ if (needLEDState) {
+ writeLEDStateRect(cp->ledState());
+ needLEDState = false;
+ }
+
+ if (needQEMUKeyEvent) {
+ writeQEMUKeyEventRect();
+ needQEMUKeyEvent = false;
+ }
}
void SMsgWriter::writeNoDataRects()
@@ -525,3 +567,34 @@ void SMsgWriter::writeSetCursorWithAlphaRect(int width, int height,
data += 4;
}
}
+
+void SMsgWriter::writeLEDStateRect(rdr::U8 state)
+{
+ if (!cp->supportsLEDState)
+ throw Exception("Client does not support LED state updates");
+ if (cp->ledState() == ledUnknown)
+ throw Exception("Server does not support LED state updates");
+ if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
+ throw Exception("SMsgWriter::writeLEDStateRect: nRects out of sync");
+
+ os->writeS16(0);
+ os->writeS16(0);
+ os->writeU16(0);
+ os->writeU16(0);
+ os->writeU32(pseudoEncodingLEDState);
+ os->writeU8(state);
+}
+
+void SMsgWriter::writeQEMUKeyEventRect()
+{
+ if (!cp->supportsQEMUKeyEvent)
+ throw Exception("Client does not support QEMU extended key events");
+ if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
+ throw Exception("SMsgWriter::writeQEMUKeyEventRect: nRects out of sync");
+
+ os->writeS16(0);
+ os->writeS16(0);
+ os->writeU16(0);
+ os->writeU16(0);
+ os->writeU32(pseudoEncodingQEMUKeyEvent);
+}
diff --git a/common/rfb/SMsgWriter.h b/common/rfb/SMsgWriter.h
index 548b8e8e..7660b118 100644
--- a/common/rfb/SMsgWriter.h
+++ b/common/rfb/SMsgWriter.h
@@ -82,6 +82,12 @@ namespace rfb {
bool writeSetXCursor();
bool writeSetCursorWithAlpha();
+ // Same for LED state message
+ bool writeLEDState();
+
+ // And QEMU keyboard event handshake
+ bool writeQEMUKeyEvent();
+
// needFakeUpdate() returns true when an immediate update is needed in
// order to flush out pseudo-rectangles to the client.
bool needFakeUpdate();
@@ -131,6 +137,8 @@ namespace rfb {
void writeSetCursorWithAlphaRect(int width, int height,
int hotspotX, int hotspotY,
const rdr::U8* data);
+ void writeLEDStateRect(rdr::U8 state);
+ void writeQEMUKeyEventRect();
ConnParams* cp;
rdr::OutStream* os;
@@ -141,10 +149,11 @@ namespace rfb {
bool needSetDesktopSize;
bool needExtendedDesktopSize;
bool needSetDesktopName;
- bool needLastRect;
bool needSetCursor;
bool needSetXCursor;
bool needSetCursorWithAlpha;
+ bool needLEDState;
+ bool needQEMUKeyEvent;
typedef struct {
rdr::U16 reason, result;
diff --git a/common/rfb/VNCSConnectionST.cxx b/common/rfb/VNCSConnectionST.cxx
index c92bdb46..cf37fddd 100644
--- a/common/rfb/VNCSConnectionST.cxx
+++ b/common/rfb/VNCSConnectionST.cxx
@@ -34,10 +34,12 @@
#include <rfb/Security.h>
#include <rfb/screenTypes.h>
#include <rfb/fenceTypes.h>
+#include <rfb/ledStates.h>
#include <rfb/ServerCore.h>
#include <rfb/ComparingUpdateTracker.h>
#include <rfb/KeyRemapper.h>
#include <rfb/Encoder.h>
+#define XK_LATIN1
#define XK_MISCELLANY
#define XK_XKB_KEYS
#include <rfb/keysymdef.h>
@@ -100,11 +102,18 @@ VNCSConnectionST::~VNCSConnectionST()
(closeReason.buf) ? closeReason.buf : "");
// Release any keys the client still had pressed
- std::set<rdr::U32>::iterator i;
- for (i=pressedKeys.begin(); i!=pressedKeys.end(); i++) {
- vlog.debug("Releasing key 0x%x on client disconnect", *i);
- server->desktop->keyEvent(*i, false);
+ while (!pressedKeys.empty()) {
+ rdr::U32 keysym, keycode;
+
+ keysym = pressedKeys.begin()->second;
+ keycode = pressedKeys.begin()->first;
+ pressedKeys.erase(pressedKeys.begin());
+
+ vlog.debug("Releasing key 0x%x / 0x%x on client disconnect",
+ keysym, keycode);
+ server->desktop->keyEvent(keysym, keycode, false);
}
+
if (server->pointerClient == this)
server->pointerClient = 0;
@@ -313,6 +322,16 @@ void VNCSConnectionST::setCursorOrClose()
}
+void VNCSConnectionST::setLEDStateOrClose(unsigned int state)
+{
+ try {
+ setLEDState(state);
+ } catch(rdr::Exception& e) {
+ close(e.str());
+ }
+}
+
+
int VNCSConnectionST::checkIdleTimeout()
{
int idleTimeout = rfb::Server::idleTimeout;
@@ -421,6 +440,7 @@ void VNCSConnectionST::authSuccess()
cp.height = server->pb->height();
cp.screenLayout = server->screenLayout;
cp.setName(server->getName());
+ cp.setLEDState(server->ledState);
// - Set the default pixel format
cp.setPF(server->pb->getPF());
@@ -530,12 +550,12 @@ public:
~VNCSConnectionSTShiftPresser() {
if (pressed) {
vlog.debug("Releasing fake Shift_L");
- desktop->keyEvent(XK_Shift_L, false);
+ desktop->keyEvent(XK_Shift_L, 0, false);
}
}
void press() {
vlog.debug("Pressing fake Shift_L");
- desktop->keyEvent(XK_Shift_L, true);
+ desktop->keyEvent(XK_Shift_L, 0, true);
pressed = true;
}
SDesktop* desktop;
@@ -544,42 +564,142 @@ public:
// keyEvent() - record in the pressedKeys which keys were pressed. Allow
// multiple down events (for autorepeat), but only allow a single up event.
-void VNCSConnectionST::keyEvent(rdr::U32 key, bool down) {
+void VNCSConnectionST::keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down) {
+ rdr::U32 lookup;
+
lastEventTime = time(0);
server->lastUserInputTime = lastEventTime;
if (!(accessRights & AccessKeyEvents)) return;
if (!rfb::Server::acceptKeyEvents) return;
if (down)
- vlog.debug("Key pressed: 0x%x", key);
+ vlog.debug("Key pressed: 0x%x / 0x%x", keysym, keycode);
else
- vlog.debug("Key released: 0x%x", key);
+ vlog.debug("Key released: 0x%x / 0x%x", keysym, keycode);
// Remap the key if required
if (server->keyRemapper) {
rdr::U32 newkey;
- newkey = server->keyRemapper->remapKey(key);
- if (newkey != key) {
+ newkey = server->keyRemapper->remapKey(keysym);
+ if (newkey != keysym) {
vlog.debug("Key remapped to 0x%x", newkey);
- key = newkey;
+ keysym = newkey;
+ }
+ }
+
+ // Avoid lock keys if we don't know the server state
+ if ((server->ledState == ledUnknown) &&
+ ((keysym == XK_Caps_Lock) ||
+ (keysym == XK_Num_Lock) ||
+ (keysym == XK_Scroll_Lock))) {
+ vlog.debug("Ignoring lock key (e.g. caps lock)");
+ return;
+ }
+
+ // Lock key heuristics
+ // (only for clients that do not support the LED state extension)
+ if (!cp.supportsLEDState) {
+ // Always ignore ScrollLock as we don't have a heuristic
+ // for that
+ if (keysym == XK_Scroll_Lock) {
+ vlog.debug("Ignoring lock key (e.g. caps lock)");
+ return;
+ }
+
+ if (down && (server->ledState != ledUnknown)) {
+ // CapsLock synchronisation heuristic
+ // (this assumes standard interaction between CapsLock the Shift
+ // keys and normal characters)
+ if (((keysym >= XK_A) && (keysym <= XK_Z)) ||
+ ((keysym >= XK_a) && (keysym <= XK_z))) {
+ bool uppercase, shift, lock;
+
+ uppercase = (keysym >= XK_A) && (keysym <= XK_Z);
+ shift = pressedKeys.find(XK_Shift_L) != pressedKeys.end() ||
+ pressedKeys.find(XK_Shift_R) != pressedKeys.end();
+ lock = server->ledState & ledCapsLock;
+
+ if (lock == (uppercase == shift)) {
+ vlog.debug("Inserting fake CapsLock to get in sync with client");
+ server->desktop->keyEvent(XK_Caps_Lock, 0, true);
+ server->desktop->keyEvent(XK_Caps_Lock, 0, false);
+ }
+ }
+
+ // NumLock synchronisation heuristic
+ // (this is more cautious because of the differences between Unix,
+ // Windows and macOS)
+ if (((keysym >= XK_KP_Home) && (keysym <= XK_KP_Delete)) ||
+ ((keysym >= XK_KP_0) && (keysym <= XK_KP_9)) ||
+ (keysym == XK_KP_Separator) || (keysym == XK_KP_Decimal)) {
+ bool number, shift, lock;
+
+ number = ((keysym >= XK_KP_0) && (keysym <= XK_KP_9)) ||
+ (keysym == XK_KP_Separator) || (keysym == XK_KP_Decimal);
+ shift = pressedKeys.find(XK_Shift_L) != pressedKeys.end() ||
+ pressedKeys.find(XK_Shift_R) != pressedKeys.end();
+ lock = server->ledState & ledNumLock;
+
+ if (shift) {
+ // We don't know the appropriate NumLock state for when Shift
+ // is pressed as it could be one of:
+ //
+ // a) A Unix client where Shift negates NumLock
+ //
+ // b) A Windows client where Shift only cancels NumLock
+ //
+ // c) A macOS client where Shift doesn't have any effect
+ //
+ } else if (lock == (number == shift)) {
+ vlog.debug("Inserting fake NumLock to get in sync with client");
+ server->desktop->keyEvent(XK_Num_Lock, 0, true);
+ server->desktop->keyEvent(XK_Num_Lock, 0, false);
+ }
+ }
}
}
// Turn ISO_Left_Tab into shifted Tab.
VNCSConnectionSTShiftPresser shiftPresser(server->desktop);
- if (key == XK_ISO_Left_Tab) {
- if (pressedKeys.find(XK_Shift_L) == pressedKeys.end() &&
- pressedKeys.find(XK_Shift_R) == pressedKeys.end())
+ if (keysym == XK_ISO_Left_Tab) {
+ std::map<rdr::U32, rdr::U32>::const_iterator iter;
+ bool shifted;
+
+ shifted = false;
+ for (iter = pressedKeys.begin(); iter != pressedKeys.end(); ++iter) {
+ if ((iter->second == XK_Shift_L) ||
+ (iter->second == XK_Shift_R)) {
+ shifted = true;
+ break;
+ }
+ }
+
+ if (!shifted)
shiftPresser.press();
- key = XK_Tab;
+
+ keysym = XK_Tab;
}
+ // We need to be able to track keys, so generate a fake index when we
+ // aren't given a keycode
+ if (keycode == 0)
+ lookup = 0x80000000 | keysym;
+ else
+ lookup = keycode;
+
+ // We force the same keysym for an already down key for the
+ // sake of sanity
+ if (pressedKeys.find(lookup) != pressedKeys.end())
+ keysym = pressedKeys[lookup];
+
if (down) {
- pressedKeys.insert(key);
+ pressedKeys[lookup] = keysym;
} else {
- if (!pressedKeys.erase(key)) return;
+ if (!pressedKeys.erase(lookup))
+ return;
}
- server->desktop->keyEvent(key, down);
+
+ server->desktop->keyEvent(keysym, keycode, down);
}
void VNCSConnectionST::clientCutText(const char* str, int len)
@@ -754,6 +874,11 @@ void VNCSConnectionST::supportsContinuousUpdates()
writer()->writeEndOfContinuousUpdates();
}
+void VNCSConnectionST::supportsLEDState()
+{
+ writer()->writeLEDState();
+}
+
bool VNCSConnectionST::handleTimeout(Timer* t)
{
@@ -1170,6 +1295,21 @@ void VNCSConnectionST::setDesktopName(const char *name)
writeFramebufferUpdate();
}
+void VNCSConnectionST::setLEDState(unsigned int ledstate)
+{
+ if (state() != RFBSTATE_NORMAL)
+ return;
+
+ cp.setLEDState(ledstate);
+
+ if (!writer()->writeLEDState()) {
+ // No client support
+ return;
+ }
+
+ writeFramebufferUpdate();
+}
+
void VNCSConnectionST::setSocketTimeouts()
{
int timeoutms = rfb::Server::clientWaitTimeMillis;
diff --git a/common/rfb/VNCSConnectionST.h b/common/rfb/VNCSConnectionST.h
index 74a6946d..d3bec93f 100644
--- a/common/rfb/VNCSConnectionST.h
+++ b/common/rfb/VNCSConnectionST.h
@@ -27,7 +27,7 @@
#ifndef __RFB_VNCSCONNECTIONST_H__
#define __RFB_VNCSCONNECTIONST_H__
-#include <set>
+#include <map>
#include <rfb/SConnection.h>
#include <rfb/SMsgWriter.h>
#include <rfb/VNCServerST.h>
@@ -78,6 +78,7 @@ namespace rfb {
void bellOrClose();
void serverCutTextOrClose(const char *str, int len);
void setDesktopNameOrClose(const char *name);
+ void setLEDStateOrClose(unsigned int state);
// checkIdleTimeout() returns the number of milliseconds left until the
// idle timeout expires. If it has expired, the connection is closed and
@@ -135,7 +136,7 @@ namespace rfb {
virtual void clientInit(bool shared);
virtual void setPixelFormat(const PixelFormat& pf);
virtual void pointerEvent(const Point& pos, int buttonMask);
- virtual void keyEvent(rdr::U32 key, bool down);
+ virtual void keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down);
virtual void clientCutText(const char* str, int len);
virtual void framebufferUpdateRequest(const Rect& r, bool incremental);
virtual void setDesktopSize(int fb_width, int fb_height,
@@ -146,6 +147,7 @@ namespace rfb {
virtual void supportsLocalCursor();
virtual void supportsFence();
virtual void supportsContinuousUpdates();
+ virtual void supportsLEDState();
// setAccessRights() allows a security package to limit the access rights
// of a VNCSConnectioST to the server. These access rights are applied
@@ -174,6 +176,7 @@ namespace rfb {
void screenLayoutChange(rdr::U16 reason);
void setCursor();
void setDesktopName(const char *name);
+ void setLEDState(unsigned int state);
void setSocketTimeouts();
network::Socket* sock;
@@ -207,7 +210,7 @@ namespace rfb {
Region cuRegion;
EncodeManager encodeManager;
- std::set<rdr::U32> pressedKeys;
+ std::map<rdr::U32, rdr::U32> pressedKeys;
time_t lastEventTime;
time_t pointerEventTime;
diff --git a/common/rfb/VNCServer.h b/common/rfb/VNCServer.h
index 982a4ff5..c5335ad2 100644
--- a/common/rfb/VNCServer.h
+++ b/common/rfb/VNCServer.h
@@ -74,6 +74,10 @@ namespace rfb {
// setName() tells the server what desktop title to supply to clients
virtual void setName(const char* name) = 0;
+
+ // setLEDState() tells the server what the current lock keys LED
+ // state is
+ virtual void setLEDState(unsigned int state) = 0;
};
}
#endif
diff --git a/common/rfb/VNCServerST.cxx b/common/rfb/VNCServerST.cxx
index ec5e962f..43e8f3ed 100644
--- a/common/rfb/VNCServerST.cxx
+++ b/common/rfb/VNCServerST.cxx
@@ -58,6 +58,7 @@
#include <rfb/Security.h>
#include <rfb/KeyRemapper.h>
#include <rfb/util.h>
+#include <rfb/ledStates.h>
#include <rdr/types.h>
@@ -74,7 +75,7 @@ LogWriter VNCServerST::connectionsLog("Connections");
VNCServerST::VNCServerST(const char* name_, SDesktop* desktop_)
: blHosts(&blacklist), desktop(desktop_), desktopStarted(false),
- blockCounter(0), pb(0),
+ blockCounter(0), pb(0), ledState(ledUnknown),
name(strDup(name_)), pointerClient(0), comparer(0),
cursor(new Cursor(0, 0, Point(), NULL)),
renderedCursorInvalid(false),
@@ -458,6 +459,21 @@ void VNCServerST::setCursorPos(const Point& pos)
}
}
+void VNCServerST::setLEDState(unsigned int state)
+{
+ std::list<VNCSConnectionST*>::iterator ci, ci_next;
+
+ if (state == ledState)
+ return;
+
+ ledState = state;
+
+ for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
+ ci_next = ci; ci_next++;
+ (*ci)->setLEDStateOrClose(state);
+ }
+}
+
// Other public methods
void VNCServerST::approveConnection(network::Socket* sock, bool accept,
diff --git a/common/rfb/VNCServerST.h b/common/rfb/VNCServerST.h
index 00f77c73..2dfdbbdb 100644
--- a/common/rfb/VNCServerST.h
+++ b/common/rfb/VNCServerST.h
@@ -101,6 +101,7 @@ namespace rfb {
virtual void setCursor(int width, int height, const Point& hotspot,
const rdr::U8* data);
virtual void setCursorPos(const Point& p);
+ virtual void setLEDState(unsigned state);
virtual void bell();
@@ -209,6 +210,7 @@ namespace rfb {
int blockCounter;
PixelBuffer* pb;
ScreenSet screenLayout;
+ unsigned int ledState;
CharArray name;
diff --git a/common/rfb/encodings.h b/common/rfb/encodings.h
index a65d863b..122afe7f 100644
--- a/common/rfb/encodings.h
+++ b/common/rfb/encodings.h
@@ -34,11 +34,13 @@ namespace rfb {
const int pseudoEncodingXCursor = -240;
const int pseudoEncodingCursor = -239;
const int pseudoEncodingDesktopSize = -223;
+ const int pseudoEncodingLEDState = -261;
const int pseudoEncodingExtendedDesktopSize = -308;
const int pseudoEncodingDesktopName = -307;
const int pseudoEncodingFence = -312;
const int pseudoEncodingContinuousUpdates = -313;
const int pseudoEncodingCursorWithAlpha = -314;
+ const int pseudoEncodingQEMUKeyEvent = -258;
// TightVNC-specific
const int pseudoEncodingLastRect = -224;
diff --git a/common/rfb/ledStates.h b/common/rfb/ledStates.h
new file mode 100644
index 00000000..ef146828
--- /dev/null
+++ b/common/rfb/ledStates.h
@@ -0,0 +1,30 @@
+/* Copyright 2016 Pierre Ossman for Cendio AB
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+#ifndef __RFB_LEDSTATES_H__
+#define __RFB_LEDSTATES_H__
+
+namespace rfb {
+
+ const unsigned int ledScrollLock = 1 << 0;
+ const unsigned int ledNumLock = 1 << 1;
+ const unsigned int ledCapsLock = 1 << 2;
+
+ const unsigned int ledUnknown = (unsigned int)-1;
+}
+
+#endif
diff --git a/common/rfb/msgTypes.h b/common/rfb/msgTypes.h
index a55e1c50..a17493cd 100644
--- a/common/rfb/msgTypes.h
+++ b/common/rfb/msgTypes.h
@@ -45,5 +45,7 @@ namespace rfb {
const int msgTypeClientFence = 248;
const int msgTypeSetDesktopSize = 251;
+
+ const int msgTypeQEMUClientMessage = 255;
}
#endif
diff --git a/common/rfb/qemuTypes.h b/common/rfb/qemuTypes.h
new file mode 100644
index 00000000..6a67f781
--- /dev/null
+++ b/common/rfb/qemuTypes.h
@@ -0,0 +1,25 @@
+/* Copyright 2017 Pierre Ossman for Cendio AB
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+#ifndef __RFB_QEMUTYPES_H__
+#define __RFB_QEMUTYPES_H__
+
+namespace rfb {
+ const int qemuExtendedKeyEvent = 0;
+ const int qemuAudio = 1;
+}
+#endif