Merge branch 'fix-warnings'
diff --git a/cmake/StaticBuild.cmake b/cmake/StaticBuild.cmake
index 6db0e14..06883c6 100644
--- a/cmake/StaticBuild.cmake
+++ b/cmake/StaticBuild.cmake
@@ -91,7 +91,7 @@
if(${CMAKE_SYSTEM_NAME} MATCHES "SunOS")
set(FLTK_LIBRARIES "${FLTK_LIBRARIES} ${X11_Xcursor_LIB} ${X11_Xfixes_LIB} -Wl,-Bstatic -lXft -Wl,-Bdynamic -lfontconfig -lXrender -lXext -R/usr/sfw/lib -L=/usr/sfw/lib -lfreetype -lsocket -lnsl")
else()
- set(FLTK_LIBRARIES "${FLTK_LIBRARIES} -Wl,-Bstatic -lXcursor -lXfixes -lXft -lfontconfig -lexpat -lfreetype -lbz2 -lXrender -lXext -lXinerama -Wl,-Bdynamic")
+ set(FLTK_LIBRARIES "${FLTK_LIBRARIES} -Wl,-Bstatic -lXcursor -lXfixes -lXft -lfontconfig -lexpat -lfreetype -lpng -lbz2 -lXrender -lXext -lXinerama -Wl,-Bdynamic")
endif()
set(FLTK_LIBRARIES "${FLTK_LIBRARIES} -lX11")
diff --git a/common/rfb/CConnection.h b/common/rfb/CConnection.h
index e0a000f..e29c033 100644
--- a/common/rfb/CConnection.h
+++ b/common/rfb/CConnection.h
@@ -134,6 +134,8 @@
// Identities, to determine the unique(ish) name of the server.
const char* getServerName() const { return serverName.buf; }
+ bool isSecure() const { return csecurity ? csecurity->isSecure() : false; }
+
enum stateEnum {
RFBSTATE_UNINITIALISED,
RFBSTATE_PROTOCOL_VERSION,
diff --git a/common/rfb/CMsgHandler.cxx b/common/rfb/CMsgHandler.cxx
index 11c979a..b89bc18 100644
--- a/common/rfb/CMsgHandler.cxx
+++ b/common/rfb/CMsgHandler.cxx
@@ -75,6 +75,11 @@
cp.supportsContinuousUpdates = true;
}
+void CMsgHandler::supportsQEMUKeyEvent()
+{
+ cp.supportsQEMUKeyEvent = true;
+}
+
void CMsgHandler::framebufferUpdateStart()
{
}
@@ -82,3 +87,8 @@
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 993276e..903ee15 100644
--- a/common/rfb/CMsgHandler.h
+++ b/common/rfb/CMsgHandler.h
@@ -55,6 +55,7 @@
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 @@
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 9abe3f2..eee6d27 100644
--- a/common/rfb/CMsgReader.cxx
+++ b/common/rfb/CMsgReader.cxx
@@ -109,6 +109,11 @@
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 @@
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 7b52033..9963827 100644
--- a/common/rfb/CMsgReader.h
+++ b/common/rfb/CMsgReader.h
@@ -65,6 +65,7 @@
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 2142d51..84a0d1f 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>
@@ -72,9 +73,9 @@
rdr::U32 encodings[encodingMax+3];
if (cp->supportsLocalCursor) {
- encodings[nEncodings++] = pseudoEncodingXCursor;
- encodings[nEncodings++] = pseudoEncodingCursor;
encodings[nEncodings++] = pseudoEncodingCursorWithAlpha;
+ encodings[nEncodings++] = pseudoEncodingCursor;
+ encodings[nEncodings++] = pseudoEncodingXCursor;
}
if (cp->supportsDesktopResize)
encodings[nEncodings++] = pseudoEncodingDesktopSize;
@@ -82,10 +83,13 @@
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 @@
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();
+ }
}
diff --git a/common/rfb/CMsgWriter.h b/common/rfb/CMsgWriter.h
index 858bc15..eb9b112 100644
--- a/common/rfb/CMsgWriter.h
+++ b/common/rfb/CMsgWriter.h
@@ -55,7 +55,7 @@
// 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, int len);
diff --git a/common/rfb/CSecurity.h b/common/rfb/CSecurity.h
index 36da5c7..3fedc50 100644
--- a/common/rfb/CSecurity.h
+++ b/common/rfb/CSecurity.h
@@ -49,6 +49,7 @@
virtual void destroy() { delete this; }
virtual int getType() const = 0;
virtual const char* description() const = 0;
+ virtual bool isSecure() const { return false; }
/*
* Use variable directly instead of dumb get/set methods.
diff --git a/common/rfb/CSecurityPlain.cxx b/common/rfb/CSecurityPlain.cxx
index 0320ce2..8e383c3 100644
--- a/common/rfb/CSecurityPlain.cxx
+++ b/common/rfb/CSecurityPlain.cxx
@@ -33,7 +33,7 @@
CharArray username;
CharArray password;
- (CSecurity::upg)->getUserPasswd(&username.buf, &password.buf);
+ (CSecurity::upg)->getUserPasswd(cc->isSecure(), &username.buf, &password.buf);
// Return the response to the server
os->writeU32(strlen(username.buf));
diff --git a/common/rfb/CSecurityStack.cxx b/common/rfb/CSecurityStack.cxx
index cfc60fd..47c3f6d 100644
--- a/common/rfb/CSecurityStack.cxx
+++ b/common/rfb/CSecurityStack.cxx
@@ -63,3 +63,12 @@
return res;
}
+
+bool CSecurityStack::isSecure() const
+{
+ if (state0 && state0->isSecure())
+ return true;
+ if (state == 1 && state1 && state1->isSecure())
+ return true;
+ return false;
+}
diff --git a/common/rfb/CSecurityStack.h b/common/rfb/CSecurityStack.h
index a76b3fe..a16003f 100644
--- a/common/rfb/CSecurityStack.h
+++ b/common/rfb/CSecurityStack.h
@@ -32,6 +32,7 @@
virtual bool processMsg(CConnection* cc);
virtual int getType() const {return type;};
virtual const char* description() const {return name;}
+ virtual bool isSecure() const;
protected:
int state;
CSecurity* state0;
diff --git a/common/rfb/CSecurityTLS.cxx b/common/rfb/CSecurityTLS.cxx
index 8a053e3..8116e9c 100644
--- a/common/rfb/CSecurityTLS.cxx
+++ b/common/rfb/CSecurityTLS.cxx
@@ -254,6 +254,11 @@
if (gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, cert_cred) != GNUTLS_E_SUCCESS)
throw AuthFailureException("gnutls_credentials_set failed");
+ if (gnutls_server_name_set(session, GNUTLS_NAME_DNS,
+ client->getServerName(),
+ strlen(client->getServerName())) != GNUTLS_E_SUCCESS)
+ vlog.error("Failed to configure the server name for TLS handshake");
+
vlog.debug("X509 session has been set");
}
}
@@ -332,6 +337,9 @@
if (status & GNUTLS_CERT_SIGNER_NOT_CA)
vlog.debug("server cert signer not CA");
+ if (status & GNUTLS_CERT_INSECURE_ALGORITHM)
+ throw AuthFailureException("The server certificate uses an insecure algorithm");
+
if ((status & (~allowed_errors)) != 0) {
/* No other errors are allowed */
vlog.debug("GNUTLS status of certificate verification: %u", status);
diff --git a/common/rfb/CSecurityTLS.h b/common/rfb/CSecurityTLS.h
index 57d964d..e726d1e 100644
--- a/common/rfb/CSecurityTLS.h
+++ b/common/rfb/CSecurityTLS.h
@@ -48,6 +48,7 @@
virtual int getType() const { return anon ? secTypeTLSNone : secTypeX509None; }
virtual const char* description() const
{ return anon ? "TLS Encryption without VncAuth" : "X509 Encryption without VncAuth"; }
+ virtual bool isSecure() const { return !anon; }
static void setDefaults();
static StringParameter X509CA;
diff --git a/common/rfb/CSecurityVeNCrypt.cxx b/common/rfb/CSecurityVeNCrypt.cxx
index a15da4a..f9597cc 100644
--- a/common/rfb/CSecurityVeNCrypt.cxx
+++ b/common/rfb/CSecurityVeNCrypt.cxx
@@ -191,3 +191,16 @@
return csecurity->processMsg(cc);
}
+const char* CSecurityVeNCrypt::description() const
+{
+ if (csecurity)
+ return csecurity->description();
+ return "VeNCrypt";
+}
+
+bool CSecurityVeNCrypt::isSecure() const
+{
+ if (csecurity && csecurity->isSecure())
+ return true;
+ return false;
+}
diff --git a/common/rfb/CSecurityVeNCrypt.h b/common/rfb/CSecurityVeNCrypt.h
index 55d0744..6d978c7 100644
--- a/common/rfb/CSecurityVeNCrypt.h
+++ b/common/rfb/CSecurityVeNCrypt.h
@@ -38,7 +38,8 @@
~CSecurityVeNCrypt();
virtual bool processMsg(CConnection* cc);// { return true; }
int getType() const {return chosenType;}
- virtual const char* description() const { return secTypeName(chosenType); }
+ virtual const char* description() const;
+ virtual bool isSecure() const;
protected:
CSecurity *csecurity;
diff --git a/common/rfb/CSecurityVncAuth.cxx b/common/rfb/CSecurityVncAuth.cxx
index f44e56e..46463e0 100644
--- a/common/rfb/CSecurityVncAuth.cxx
+++ b/common/rfb/CSecurityVncAuth.cxx
@@ -49,7 +49,7 @@
rdr::U8 challenge[vncAuthChallengeSize];
is->readBytes(challenge, vncAuthChallengeSize);
PlainPasswd passwd;
- (CSecurity::upg)->getUserPasswd(0, &passwd.buf);
+ (CSecurity::upg)->getUserPasswd(cc->isSecure(), 0, &passwd.buf);
// Calculate the correct response
rdr::U8 key[8];
diff --git a/common/rfb/ConnParams.cxx b/common/rfb/ConnParams.cxx
index 9ee1d9c..23f02ed 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 @@
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 @@
supportsExtendedDesktopSize = false;
supportsLocalXCursor = false;
supportsLastRect = false;
+ supportsQEMUKeyEvent = false;
compressLevel = -1;
qualityLevel = -1;
fineQualityLevel = -1;
@@ -141,6 +145,11 @@
case pseudoEncodingLastRect:
supportsLastRect = true;
break;
+ case pseudoEncodingLEDState:
+ supportsLEDState = true;
+ case pseudoEncodingQEMUKeyEvent:
+ supportsQEMUKeyEvent = true;
+ break;
case pseudoEncodingFence:
supportsFence = true;
break;
@@ -183,3 +192,8 @@
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 5e53893..b322293 100644
--- a/common/rfb/ConnParams.h
+++ b/common/rfb/ConnParams.h
@@ -84,6 +84,9 @@
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 @@
bool supportsExtendedDesktopSize;
bool supportsDesktopRename;
bool supportsLastRect;
+ bool supportsLEDState;
+ bool supportsQEMUKeyEvent;
bool supportsSetDesktopSize;
bool supportsFence;
@@ -111,6 +116,7 @@
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 727f69b..6c07284 100644
--- a/common/rfb/InputHandler.h
+++ b/common/rfb/InputHandler.h
@@ -32,7 +32,8 @@
class InputHandler {
public:
virtual ~InputHandler() {}
- virtual void keyEvent(rdr::U32 __unused_attr key,
+ virtual void keyEvent(rdr::U32 __unused_attr keysym,
+ rdr::U32 __unused_attr keycode,
bool __unused_attr down) { }
virtual void pointerEvent(const Point& __unused_attr pos,
int __unused_attr buttonMask) { }
diff --git a/common/rfb/SConnection.cxx b/common/rfb/SConnection.cxx
index 85cc6e8..c5c9038 100644
--- a/common/rfb/SConnection.cxx
+++ b/common/rfb/SConnection.cxx
@@ -278,6 +278,11 @@
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 63dc314..bc43583 100644
--- a/common/rfb/SConnection.h
+++ b/common/rfb/SConnection.h
@@ -73,6 +73,7 @@
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 388b21f..c38458c 100644
--- a/common/rfb/SMsgHandler.cxx
+++ b/common/rfb/SMsgHandler.cxx
@@ -41,10 +41,13 @@
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 @@
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::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 74509e0..749f056 100644
--- a/common/rfb/SMsgHandler.h
+++ b/common/rfb/SMsgHandler.h
@@ -74,6 +74,17 @@
// 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 3c08fd6..cb71ac8 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 @@
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 @@
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 @@
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 00cb303..146b29f 100644
--- a/common/rfb/SMsgReader.h
+++ b/common/rfb/SMsgReader.h
@@ -55,6 +55,9 @@
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 bc3f439..2d4998b 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 @@
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 @@
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 @@
nRects++;
if (needSetCursorWithAlpha)
nRects++;
+ if (needLEDState)
+ nRects++;
+ if (needQEMUKeyEvent)
+ nRects++;
}
os->writeU16(nRects);
@@ -362,6 +394,16 @@
writeSetDesktopNameRect(cp->name());
needSetDesktopName = false;
}
+
+ if (needLEDState) {
+ writeLEDStateRect(cp->ledState());
+ needLEDState = false;
+ }
+
+ if (needQEMUKeyEvent) {
+ writeQEMUKeyEventRect();
+ needQEMUKeyEvent = false;
+ }
}
void SMsgWriter::writeNoDataRects()
@@ -525,3 +567,34 @@
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 6c665b6..e985941 100644
--- a/common/rfb/SMsgWriter.h
+++ b/common/rfb/SMsgWriter.h
@@ -82,6 +82,12 @@
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 @@
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 @@
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/UserPasswdGetter.h b/common/rfb/UserPasswdGetter.h
index aa72c1d..27775cc 100644
--- a/common/rfb/UserPasswdGetter.h
+++ b/common/rfb/UserPasswdGetter.h
@@ -24,7 +24,7 @@
// dialog, getpass(), etc. The user buffer pointer can be null, in which
// case no user name will be retrieved. The caller MUST delete [] the
// result(s).
- virtual void getUserPasswd(char** user, char** password)=0;
+ virtual void getUserPasswd(bool secure, char** user, char** password)=0;
virtual ~UserPasswdGetter() {}
};
diff --git a/common/rfb/VNCSConnectionST.cxx b/common/rfb/VNCSConnectionST.cxx
index d2206f9..b183c05 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>
@@ -64,6 +66,8 @@
unsigned inFlight;
};
+static Cursor emptyCursor(0, 0, Point(0, 0), NULL);
+
VNCSConnectionST::VNCSConnectionST(VNCServerST* server_, network::Socket *s,
bool reverse)
: sock(s), reverseConnection(reverse),
@@ -98,11 +102,18 @@
(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;
@@ -311,6 +322,16 @@
}
+void VNCSConnectionST::setLEDStateOrClose(unsigned int state)
+{
+ try {
+ setLEDState(state);
+ } catch(rdr::Exception& e) {
+ close(e.str());
+ }
+}
+
+
int VNCSConnectionST::checkIdleTimeout()
{
int idleTimeout = rfb::Server::idleTimeout;
@@ -357,6 +378,9 @@
void VNCSConnectionST::renderedCursorChange()
{
if (state() != RFBSTATE_NORMAL) return;
+ // Are we switching between client-side and server-side cursor?
+ if (damagedCursorRegion.is_empty() != needRenderedCursor())
+ setCursorOrClose();
if (!damagedCursorRegion.is_empty())
removeRenderedCursor = true;
if (needRenderedCursor()) {
@@ -380,7 +404,8 @@
if (state() != RFBSTATE_NORMAL)
return false;
- if (!cp.supportsLocalCursor && !cp.supportsLocalXCursor)
+ if (!cp.supportsLocalCursorWithAlpha &&
+ !cp.supportsLocalCursor && !cp.supportsLocalXCursor)
return true;
if (!server->cursorPos.equals(pointerEventPos) &&
(time(0) - pointerEventTime) > 0)
@@ -415,6 +440,7 @@
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());
@@ -524,12 +550,12 @@
~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;
@@ -538,42 +564,127 @@
// 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 = isShiftPressed();
+ 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 = isShiftPressed();
+ 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) {
+ if (!isShiftPressed())
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)
@@ -725,7 +836,8 @@
void VNCSConnectionST::supportsLocalCursor()
{
- if (cp.supportsLocalCursor || cp.supportsLocalXCursor) {
+ if (cp.supportsLocalCursorWithAlpha ||
+ cp.supportsLocalCursor || cp.supportsLocalXCursor) {
if (!damagedCursorRegion.is_empty())
removeRenderedCursor = true;
setCursor();
@@ -747,6 +859,11 @@
writer()->writeEndOfContinuousUpdates();
}
+void VNCSConnectionST::supportsLEDState()
+{
+ writer()->writeLEDState();
+}
+
bool VNCSConnectionST::handleTimeout(Timer* t)
{
@@ -764,6 +881,19 @@
return false;
}
+bool VNCSConnectionST::isShiftPressed()
+{
+ std::map<rdr::U32, rdr::U32>::const_iterator iter;
+
+ for (iter = pressedKeys.begin(); iter != pressedKeys.end(); ++iter) {
+ if (iter->second == XK_Shift_L)
+ return true;
+ if (iter->second == XK_Shift_R)
+ return true;
+ }
+
+ return false;
+}
void VNCSConnectionST::writeRTTPing()
{
@@ -1130,7 +1260,11 @@
if (state() != RFBSTATE_NORMAL)
return;
- cp.setCursor(*server->cursor);
+ // We need to blank out the client's cursor or there will be two
+ if (needRenderedCursor())
+ cp.setCursor(emptyCursor);
+ else
+ cp.setCursor(*server->cursor);
if (!writer()->writeSetCursorWithAlpha()) {
if (!writer()->writeSetCursor()) {
@@ -1159,6 +1293,21 @@
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 74a6946..9b7b14b 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 @@
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 @@
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 @@
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
@@ -158,6 +160,8 @@
// Internal methods
+ bool isShiftPressed();
+
// Congestion control
void writeRTTPing();
void handleRTTPong(const struct RTTInfo &rttInfo);
@@ -174,6 +178,7 @@
void screenLayoutChange(rdr::U16 reason);
void setCursor();
void setDesktopName(const char *name);
+ void setLEDState(unsigned int state);
void setSocketTimeouts();
network::Socket* sock;
@@ -207,7 +212,7 @@
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 982a4ff..c5335ad 100644
--- a/common/rfb/VNCServer.h
+++ b/common/rfb/VNCServer.h
@@ -74,6 +74,10 @@
// 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 ec5e962..43e8f3e 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 @@
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::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 00f77c7..2dfdbbd 100644
--- a/common/rfb/VNCServerST.h
+++ b/common/rfb/VNCServerST.h
@@ -101,6 +101,7 @@
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 @@
int blockCounter;
PixelBuffer* pb;
ScreenSet screenLayout;
+ unsigned int ledState;
CharArray name;
diff --git a/common/rfb/encodings.h b/common/rfb/encodings.h
index a65d863..122afe7 100644
--- a/common/rfb/encodings.h
+++ b/common/rfb/encodings.h
@@ -34,11 +34,13 @@
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/vncviewer/FLTKPixelBuffer.h b/common/rfb/ledStates.h
similarity index 67%
copy from vncviewer/FLTKPixelBuffer.h
copy to common/rfb/ledStates.h
index 148c626..ef14682 100644
--- a/vncviewer/FLTKPixelBuffer.h
+++ b/common/rfb/ledStates.h
@@ -1,4 +1,4 @@
-/* Copyright 2011-2014 Pierre Ossman for Cendio AB
+/* 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
@@ -15,19 +15,16 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
* USA.
*/
+#ifndef __RFB_LEDSTATES_H__
+#define __RFB_LEDSTATES_H__
-#ifndef __FLTKPIXELBUFFER_H__
-#define __FLTKPIXELBUFFER_H__
+namespace rfb {
-#include "PlatformPixelBuffer.h"
+ const unsigned int ledScrollLock = 1 << 0;
+ const unsigned int ledNumLock = 1 << 1;
+ const unsigned int ledCapsLock = 1 << 2;
-class FLTKPixelBuffer: public PlatformPixelBuffer {
-public:
- FLTKPixelBuffer(int width, int height);
- ~FLTKPixelBuffer();
-
- virtual void draw(int src_x, int src_y, int x, int y, int w, int h);
-};
-
+ const unsigned int ledUnknown = (unsigned int)-1;
+}
#endif
diff --git a/common/rfb/msgTypes.h b/common/rfb/msgTypes.h
index a55e1c5..a17493c 100644
--- a/common/rfb/msgTypes.h
+++ b/common/rfb/msgTypes.h
@@ -45,5 +45,7 @@
const int msgTypeClientFence = 248;
const int msgTypeSetDesktopSize = 251;
+
+ const int msgTypeQEMUClientMessage = 255;
}
#endif
diff --git a/vncviewer/FLTKPixelBuffer.h b/common/rfb/qemuTypes.h
similarity index 67%
rename from vncviewer/FLTKPixelBuffer.h
rename to common/rfb/qemuTypes.h
index 148c626..6a67f78 100644
--- a/vncviewer/FLTKPixelBuffer.h
+++ b/common/rfb/qemuTypes.h
@@ -1,4 +1,4 @@
-/* Copyright 2011-2014 Pierre Ossman for Cendio AB
+/* 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
@@ -15,19 +15,11 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
* USA.
*/
+#ifndef __RFB_QEMUTYPES_H__
+#define __RFB_QEMUTYPES_H__
-#ifndef __FLTKPIXELBUFFER_H__
-#define __FLTKPIXELBUFFER_H__
-
-#include "PlatformPixelBuffer.h"
-
-class FLTKPixelBuffer: public PlatformPixelBuffer {
-public:
- FLTKPixelBuffer(int width, int height);
- ~FLTKPixelBuffer();
-
- virtual void draw(int src_x, int src_y, int x, int y, int w, int h);
-};
-
-
+namespace rfb {
+ const int qemuExtendedKeyEvent = 0;
+ const int qemuAudio = 1;
+}
#endif
diff --git a/config.h.in b/config.h.in
index 345af7d..2d6db9c 100644
--- a/config.h.in
+++ b/config.h.in
@@ -11,6 +11,9 @@
/* MS Visual Studio 2008 and newer doesn't know ssize_t */
#if defined(HAVE_GNUTLS) && defined(WIN32) && !defined(__MINGW32__)
-#include <stddef.h>
-typedef size_t ssize_t;
+ #if defined(_WIN64)
+ typedef __int64 ssize_t;
+ #else
+ typedef long ssize_t;
+ #endif
#endif
diff --git a/java/com/tigervnc/rfb/CMsgReader.java b/java/com/tigervnc/rfb/CMsgReader.java
index a5cc267..a79cf84 100644
--- a/java/com/tigervnc/rfb/CMsgReader.java
+++ b/java/com/tigervnc/rfb/CMsgReader.java
@@ -314,7 +314,8 @@
new ManagedPixelBuffer(rgbaPF, width, height);
PixelFormat origPF;
- DataBufferInt buf;
+ ByteBuffer buf =
+ ByteBuffer.allocate(pb.area()*4).order(rgbaPF.getByteOrder());;
encoding = is.readS32();
@@ -323,28 +324,26 @@
handler.readAndDecodeRect(pb.getRect(), encoding, pb);
handler.cp.setPF(origPF);
- if (pb.getRect().area() == 0)
- return;
-
// ARGB with pre-multiplied alpha works best for BufferedImage
- buf = (DataBufferInt)pb.getBufferRW(pb.getRect()).getDataBuffer();
- ByteBuffer bbuf =
- ByteBuffer.allocate(pb.area()*4).order(rgbaPF.getByteOrder());
- bbuf.asIntBuffer().put(buf.getData()).flip().mark();
-
- for (int i = 0;i < pb.area();i++) {
- byte alpha = bbuf.get(bbuf.position()+3);
-
- bbuf.put(i*4+3, (byte)(bbuf.get(i*4+2)));
- bbuf.put(i*4+2, (byte)(bbuf.get(i*4+1)));
- bbuf.put(i*4+1, (byte)(bbuf.get(i*4+0)));
- bbuf.put(i*4+0, (byte)alpha);
-
- bbuf.position(bbuf.position() + 4);
+ if (pb.area() > 0) {
+ // Sometimes a zero width or height cursor is sent.
+ DataBuffer db = pb.getBuffer(pb.getRect()).getDataBuffer();
+ for (int i = 0;i < pb.area();i++)
+ buf.asIntBuffer().put(i, db.getElem(i));
}
- handler.setCursor(width, height, hotspot,
- bbuf.array());
+ for (int i = 0;i < pb.area();i++) {
+ byte alpha = buf.get(buf.position()+3);
+
+ buf.put(i*4+3, buf.get(i*4+2));
+ buf.put(i*4+2, buf.get(i*4+1));
+ buf.put(i*4+1, buf.get(i*4+0));
+ buf.put(i*4+0, alpha);
+
+ buf.position(buf.position() + 4);
+ }
+
+ handler.setCursor(width, height, hotspot, buf.array());
}
protected void readSetDesktopName(int x, int y, int w, int h)
diff --git a/java/com/tigervnc/rfb/CMsgWriter.java b/java/com/tigervnc/rfb/CMsgWriter.java
index 7838da3..ea2b7c9 100644
--- a/java/com/tigervnc/rfb/CMsgWriter.java
+++ b/java/com/tigervnc/rfb/CMsgWriter.java
@@ -65,9 +65,10 @@
int[] encodings = new int[Encodings.encodingMax+3];
if (cp.supportsLocalCursor) {
- encodings[nEncodings++] = Encodings.pseudoEncodingXCursor;
+ if (cp.supportsLocalCursorWithAlpha)
+ encodings[nEncodings++] = Encodings.pseudoEncodingCursorWithAlpha;
encodings[nEncodings++] = Encodings.pseudoEncodingCursor;
- encodings[nEncodings++] = Encodings.pseudoEncodingCursorWithAlpha;
+ encodings[nEncodings++] = Encodings.pseudoEncodingXCursor;
}
if (cp.supportsDesktopResize)
encodings[nEncodings++] = Encodings.pseudoEncodingDesktopSize;
diff --git a/java/com/tigervnc/rfb/CSecurityTLS.java b/java/com/tigervnc/rfb/CSecurityTLS.java
index 4b20e0b..733e97d 100644
--- a/java/com/tigervnc/rfb/CSecurityTLS.java
+++ b/java/com/tigervnc/rfb/CSecurityTLS.java
@@ -3,7 +3,7 @@
* Copyright (C) 2005 Martin Koegler
* Copyright (C) 2010 m-privacy GmbH
* Copyright (C) 2010 TigerVNC Team
- * Copyright (C) 2011-2015 Brian P. Hinz
+ * Copyright (C) 2011-2017 Brian P. Hinz
* Copyright (C) 2015 D. R. Commander. All Rights Reserved.
*
* This is free software; you can redistribute it and/or modify
@@ -211,27 +211,15 @@
for (TrustManager m : tmf.getTrustManagers())
if (m instanceof X509TrustManager)
for (X509Certificate c : ((X509TrustManager)m).getAcceptedIssuers())
- ks.setCertificateEntry(c.getSubjectX500Principal().getName(), c);
- File castore = new File(FileUtils.getVncHomeDir()+"x509_savedcerts.pem");
- if (castore.exists() && castore.canRead()) {
- InputStream caStream = new MyFileInputStream(castore);
- Collection<? extends Certificate> cacerts =
- cf.generateCertificates(caStream);
- for (Certificate cert : cacerts) {
- String dn =
- ((X509Certificate)cert).getSubjectX500Principal().getName();
- ks.setCertificateEntry(dn, (X509Certificate)cert);
- }
- }
+ ks.setCertificateEntry(getThumbprint((X509Certificate)c), c);
File cacert = new File(cafile);
if (cacert.exists() && cacert.canRead()) {
InputStream caStream = new MyFileInputStream(cacert);
Collection<? extends Certificate> cacerts =
cf.generateCertificates(caStream);
for (Certificate cert : cacerts) {
- String dn =
- ((X509Certificate)cert).getSubjectX500Principal().getName();
- ks.setCertificateEntry(dn, (X509Certificate)cert);
+ String thumbprint = getThumbprint((X509Certificate)cert);
+ ks.setCertificateEntry(thumbprint, (X509Certificate)cert);
}
}
PKIXBuilderParameters params =
@@ -264,19 +252,25 @@
public void checkServerTrusted(X509Certificate[] chain, String authType)
throws CertificateException
{
- MessageDigest md = null;
+ Collection<? extends Certificate> certs = null;
+ X509Certificate cert = chain[0];
+ String thumbprint = getThumbprint(cert);
+ File vncDir = new File(FileUtils.getVncHomeDir());
+ File certFile = new File(vncDir, "x509_savedcerts.pem");
+ CertificateFactory cf = CertificateFactory.getInstance("X.509");
+ if (vncDir.exists() && certFile.exists() && certFile.canRead()) {
+ InputStream certStream = new MyFileInputStream(certFile);
+ certs = cf.generateCertificates(certStream);
+ for (Certificate c : certs)
+ if (thumbprint.equals(getThumbprint((X509Certificate)c)))
+ return;
+ }
try {
- md = MessageDigest.getInstance("SHA-1");
- verifyHostname(chain[0]);
+ verifyHostname(cert);
tm.checkServerTrusted(chain, authType);
} catch (java.lang.Exception e) {
if (e.getCause() instanceof CertPathBuilderException) {
Object[] answer = {"YES", "NO"};
- X509Certificate cert = chain[0];
- md.update(cert.getEncoded());
- String thumbprint =
- DatatypeConverter.printHexBinary(md.digest());
- thumbprint = thumbprint.replaceAll("..(?!$)", "$0 ");
int ret = JOptionPane.showOptionDialog(null,
"This certificate has been signed by an unknown authority\n"+
"\n"+
@@ -287,20 +281,17 @@
" Signature Algorithm: "+cert.getPublicKey().getAlgorithm()+"\n"+
" Not Valid Before: "+cert.getNotBefore()+"\n"+
" Not Valid After: "+cert.getNotAfter()+"\n"+
- " SHA1 Fingerprint: "+thumbprint+"\n"+
+ " SHA1 Fingerprint: "+getThumbprint(cert)+"\n"+
"\n"+
"Do you want to save it and continue?",
"Certificate Issuer Unknown",
JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE,
null, answer, answer[0]);
if (ret == JOptionPane.YES_OPTION) {
- Collection<? extends X509Certificate> cacerts = null;
- File vncDir = new File(FileUtils.getVncHomeDir());
- File caFile = new File(vncDir, "x509_savedcerts.pem");
try {
if (!vncDir.exists())
vncDir.mkdir();
- if (!caFile.createNewFile()) {
+ if (!certFile.exists() && !certFile.createNewFile()) {
vlog.error("Certificate save failed.");
return;
}
@@ -309,31 +300,24 @@
vlog.error("Certificate save failed: "+ioe.getMessage());
return;
}
- InputStream caStream = new MyFileInputStream(caFile);
- CertificateFactory cf =
- CertificateFactory.getInstance("X.509");
- cacerts =
- (Collection <? extends X509Certificate>)cf.generateCertificates(caStream);
- for (int i = 0; i < chain.length; i++) {
- if (cacerts == null || !cacerts.contains(chain[i])) {
- byte[] der = chain[i].getEncoded();
- String pem = DatatypeConverter.printBase64Binary(der);
- pem = pem.replaceAll("(.{64})", "$1\n");
- FileWriter fw = null;
+ if (certs == null || !certs.contains(cert)) {
+ byte[] der = cert.getEncoded();
+ String pem = DatatypeConverter.printBase64Binary(der);
+ pem = pem.replaceAll("(.{64})", "$1\n");
+ FileWriter fw = null;
+ try {
+ fw = new FileWriter(certFile.getAbsolutePath(), true);
+ fw.write("-----BEGIN CERTIFICATE-----\n");
+ fw.write(pem+"\n");
+ fw.write("-----END CERTIFICATE-----\n");
+ } catch (IOException ioe) {
+ throw new Exception(ioe.getMessage());
+ } finally {
try {
- fw = new FileWriter(caFile.getAbsolutePath(), true);
- fw.write("-----BEGIN CERTIFICATE-----\n");
- fw.write(pem+"\n");
- fw.write("-----END CERTIFICATE-----\n");
- } catch (IOException ioe) {
- throw new Exception(ioe.getMessage());
- } finally {
- try {
- if (fw != null)
- fw.close();
- } catch(IOException ioe2) {
- throw new Exception(ioe2.getMessage());
- }
+ if (fw != null)
+ fw.close();
+ } catch(IOException ioe2) {
+ throw new Exception(ioe2.getMessage());
}
}
}
@@ -351,6 +335,22 @@
return tm.getAcceptedIssuers();
}
+ private String getThumbprint(X509Certificate cert)
+ {
+ String thumbprint = null;
+ try {
+ MessageDigest md = MessageDigest.getInstance("SHA-1");
+ md.update(cert.getEncoded());
+ thumbprint = DatatypeConverter.printHexBinary(md.digest());
+ thumbprint = thumbprint.replaceAll("..(?!$)", "$0 ");
+ } catch(CertificateEncodingException e) {
+ throw new SystemException(e.getMessage());
+ } catch(NoSuchAlgorithmException e) {
+ throw new SystemException(e.getMessage());
+ }
+ return thumbprint;
+ }
+
private void verifyHostname(X509Certificate cert)
throws CertificateParsingException
{
diff --git a/java/com/tigervnc/rfb/ConnParams.java b/java/com/tigervnc/rfb/ConnParams.java
index fe52770..ce3af71 100644
--- a/java/com/tigervnc/rfb/ConnParams.java
+++ b/java/com/tigervnc/rfb/ConnParams.java
@@ -39,6 +39,7 @@
majorVersion = 0; minorVersion = 0;
width = 0; height = 0; useCopyRect = false;
supportsLocalCursor = false; supportsLocalXCursor = false;
+ supportsLocalCursorWithAlpha = false;
supportsDesktopResize = false; supportsExtendedDesktopSize = false;
supportsDesktopRename = false; supportsLastRect = false;
supportsSetDesktopSize = false; supportsFence = false;
@@ -128,6 +129,7 @@
{
useCopyRect = false;
supportsLocalCursor = false;
+ supportsLocalCursorWithAlpha = false;
supportsDesktopResize = false;
supportsExtendedDesktopSize = false;
supportsLocalXCursor = false;
@@ -151,6 +153,9 @@
case Encodings.pseudoEncodingXCursor:
supportsLocalXCursor = true;
break;
+ case Encodings.pseudoEncodingCursorWithAlpha:
+ supportsLocalCursorWithAlpha = true;
+ break;
case Encodings.pseudoEncodingDesktopSize:
supportsDesktopResize = true;
break;
@@ -213,6 +218,7 @@
public boolean supportsLocalCursor;
public boolean supportsLocalXCursor;
+ public boolean supportsLocalCursorWithAlpha;
public boolean supportsDesktopResize;
public boolean supportsExtendedDesktopSize;
public boolean supportsDesktopRename;
diff --git a/java/com/tigervnc/rfb/ManagedPixelBuffer.java b/java/com/tigervnc/rfb/ManagedPixelBuffer.java
index 6e14b92..028eadc 100644
--- a/java/com/tigervnc/rfb/ManagedPixelBuffer.java
+++ b/java/com/tigervnc/rfb/ManagedPixelBuffer.java
@@ -18,6 +18,8 @@
package com.tigervnc.rfb;
+import java.awt.image.*;
+
public class ManagedPixelBuffer extends FullFramePixelBuffer {
public ManagedPixelBuffer() {
@@ -43,9 +45,15 @@
final void checkDataSize() {
int new_datasize = width_ * height_;
if (datasize < new_datasize) {
- vlog.debug("reallocating managed buffer ("+width_+"x"+height_+")");
- if (format != null)
- data = PixelFormat.getColorModel(format).createCompatibleWritableRaster(width_, height_);
+ if (data != null) {
+ datasize = 0; data = null;
+ }
+ if (new_datasize > 0) {
+ ColorModel cm = format.getColorModel();
+ data = cm.createCompatibleWritableRaster(width_, height_);
+ image = new BufferedImage(cm, data, cm.isAlphaPremultiplied(), null);
+ datasize = new_datasize;
+ }
}
}
diff --git a/java/com/tigervnc/vncviewer/CConn.java b/java/com/tigervnc/vncviewer/CConn.java
index c53f805..85bdcb6 100644
--- a/java/com/tigervnc/vncviewer/CConn.java
+++ b/java/com/tigervnc/vncviewer/CConn.java
@@ -104,6 +104,11 @@
currentEncoding = encNum;
cp.supportsLocalCursor = true;
+ if (VncViewer.os.contains("windows"))
+ // JRE on Windows does not support alpha cursor
+ cp.supportsLocalCursorWithAlpha = false;
+ else
+ cp.supportsLocalCursorWithAlpha = true;
cp.supportsDesktopResize = true;
cp.supportsExtendedDesktopSize = true;
diff --git a/java/com/tigervnc/vncviewer/Parameters.java b/java/com/tigervnc/vncviewer/Parameters.java
index 50e26cb..2f8a585 100644
--- a/java/com/tigervnc/vncviewer/Parameters.java
+++ b/java/com/tigervnc/vncviewer/Parameters.java
@@ -295,6 +295,7 @@
CSecurityTLS.X509CA,
CSecurityTLS.X509CRL,
SecurityClient.secTypes,
+ dotWhenNoCursor,
autoSelect,
fullColor,
lowColorLevel,
diff --git a/java/com/tigervnc/vncviewer/VncViewer.java b/java/com/tigervnc/vncviewer/VncViewer.java
index e123499..d6a3930 100644
--- a/java/com/tigervnc/vncviewer/VncViewer.java
+++ b/java/com/tigervnc/vncviewer/VncViewer.java
@@ -81,7 +81,7 @@
public static final InputStream timestamp =
VncViewer.class.getResourceAsStream("timestamp");
public static final String os =
- System.getProperty("os.name").toLowerCase();
+ System.getProperty("os.name").toLowerCase(Locale.ENGLISH);
private static VncViewer applet;
private String defaultServerName;
@@ -173,7 +173,8 @@
}
// Override defaults with command-line options
- for (int i = 0; i < argv.length; i++) {
+ int i = 0;
+ for (; i < argv.length; i++) {
if (argv[i].length() == 0)
continue;
@@ -191,9 +192,6 @@
continue;
}
- if (Configuration.setParam(argv[i]))
- continue;
-
if (argv[i].charAt(0) == '-') {
if (i+1 < argv.length) {
if (Configuration.setParam(argv[i].substring(1), argv[i+1])) {
@@ -201,6 +199,9 @@
continue;
}
}
+ if (Configuration.setParam(argv[i]))
+ continue;
+
usage();
}
diff --git a/media/insecure.svg b/media/insecure.svg
new file mode 100644
index 0000000..1c5f6fa
--- /dev/null
+++ b/media/insecure.svg
@@ -0,0 +1,107 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="16"
+ height="16"
+ viewBox="0 0 4.2333332 4.2333335"
+ version="1.1"
+ id="svg8"
+ inkscape:version="0.92+devel unknown"
+ sodipodi:docname="insecure.svg">
+ <defs
+ id="defs2" />
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="16"
+ inkscape:cx="4.5653262"
+ inkscape:cy="11.192284"
+ inkscape:document-units="mm"
+ inkscape:current-layer="layer1"
+ inkscape:document-rotation="0"
+ showgrid="true"
+ units="px"
+ inkscape:window-width="1920"
+ inkscape:window-height="1136"
+ inkscape:window-x="1920"
+ inkscape:window-y="27"
+ inkscape:window-maximized="1">
+ <inkscape:grid
+ type="xygrid"
+ id="grid10" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata5">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(0,-292.76665)">
+ <rect
+ style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.13229166;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:0.26458333, 0.79374999"
+ id="rect878"
+ width="4.2333331"
+ height="4.2333331"
+ x="0"
+ y="292.76666" />
+ <rect
+ style="fill:#ff0000;stroke-width:0.13229166;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:0.26458333, 0.79375"
+ id="rect926"
+ width="4.2333331"
+ height="4.2333331"
+ x="-5.5511151e-17"
+ y="292.76666" />
+ <rect
+ style="fill:#000000;fill-opacity:1;stroke-width:0.10103943;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:0.20207887, 0.60623662"
+ id="rect12-3"
+ width="3.175"
+ height="1.8520833"
+ x="0.52916664"
+ y="294.61874"
+ ry="0.26457277" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#000000;stroke-width:0.13229166;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:0.26458333, 0.79374999"
+ d="m 2.1166667,293.29579 a 1.0583332,1.0583332 0 0 0 -1.0583334,1.05833 v 0.79375 h 0.3601848 a 1.0583332,1.0583332 0 0 0 0.6981486,0.26458 1.0583332,1.0583332 0 0 0 0.6981486,-0.26458 H 3.175 v -0.79375 a 1.0583332,1.0583332 0 0 0 -1.0583333,-1.05833 z m 0,0.52916 a 0.52916664,0.52916664 0 0 1 0.5291666,0.52917 v 0.79375 H 1.5875 v -0.79375 a 0.52916664,0.52916664 0 0 1 0.5291667,-0.52917 z"
+ id="path913" />
+ <rect
+ style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.30752403;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:0.61504807, 1.84514421"
+ id="rect871"
+ width="4.4979167"
+ height="0.52916664"
+ x="-209.4492"
+ y="209.48177"
+ transform="rotate(-45)" />
+ <rect
+ transform="rotate(-45)"
+ y="210.0107"
+ x="-209.26234"
+ height="0.52916664"
+ width="4.6772137"
+ id="rect875"
+ style="fill:#ff0000;fill-opacity:1;stroke:none;stroke-width:0.30752403;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:0.61504807, 1.84514421"
+ ry="0" />
+ </g>
+</svg>
diff --git a/media/insecure.xpm b/media/insecure.xpm
new file mode 100644
index 0000000..f5053fe
--- /dev/null
+++ b/media/insecure.xpm
@@ -0,0 +1,71 @@
+/* XPM */
+static char *insecure[] = {
+/* columns rows colors chars-per-pixel */
+"15 15 50 1 ",
+" c black",
+". c #020000",
+"X c #050000",
+"o c #080000",
+"O c #0A0000",
+"+ c #0C0000",
+"@ c #0D0000",
+"# c #0F0000",
+"$ c #100000",
+"% c #110000",
+"& c #120000",
+"* c #140000",
+"= c #290000",
+"- c #330000",
+"; c #370000",
+": c #430000",
+"> c #560000",
+", c #620000",
+"< c #660000",
+"1 c #6C0000",
+"2 c #7D0000",
+"3 c #800000",
+"4 c #810000",
+"5 c #840000",
+"6 c #870000",
+"7 c #950000",
+"8 c #A20000",
+"9 c #AB0000",
+"0 c #B30000",
+"q c #B40000",
+"w c #C00000",
+"e c #C40000",
+"r c #CD0000",
+"t c #DC0000",
+"y c #DD0000",
+"u c #DF0000",
+"i c #E40000",
+"p c #E50000",
+"a c #E60000",
+"s c #EA0000",
+"d c #EB0000",
+"f c #ED0000",
+"g c #F00000",
+"h c #F40000",
+"j c #F90000",
+"k c #FA0000",
+"l c #FB0000",
+"z c #FC0000",
+"x c #FD0000",
+"c c red",
+/* pixels */
+"ccccccccccccccc",
+"ccccccjpkcr:fcc",
+"ccccz6+ @1$ rcc",
+"cccc2 O-o wccc",
+"cccdX%tr# 4cccc",
+"ccce >r& 7czccc",
+"ccq< =O 8cg60cc",
+"cs. 3cg; ac",
+"cu 3cg; uc",
+"cu 3cg; uc",
+"c0 3cg; uc",
+"r$ 3cg; uc",
+", 5cg; *hc",
+"l9lxiyyyyyyyhcc",
+"ccccccccccccccc"
+};
diff --git a/media/secure.svg b/media/secure.svg
new file mode 100644
index 0000000..4da1075
--- /dev/null
+++ b/media/secure.svg
@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="16"
+ height="16"
+ viewBox="0 0 4.2333332 4.2333335"
+ version="1.1"
+ id="svg8"
+ inkscape:version="0.92+devel unknown"
+ sodipodi:docname="secure.svg">
+ <defs
+ id="defs2" />
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="32"
+ inkscape:cx="19.34416"
+ inkscape:cy="11.532874"
+ inkscape:document-units="mm"
+ inkscape:current-layer="layer1"
+ inkscape:document-rotation="0"
+ showgrid="true"
+ units="px"
+ inkscape:window-width="1920"
+ inkscape:window-height="1136"
+ inkscape:window-x="1920"
+ inkscape:window-y="27"
+ inkscape:window-maximized="1">
+ <inkscape:grid
+ type="xygrid"
+ id="grid10" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata5">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(0,-292.76665)">
+ <rect
+ style="fill:#00ff00;stroke-width:0.13229166;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:0.26458333, 0.79374999000000002"
+ id="rect926"
+ width="4.2333331"
+ height="4.2333331"
+ x="0"
+ y="292.76666" />
+ <rect
+ style="fill:#000000;fill-opacity:1;stroke-width:0.10103943;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:0.20207887, 0.60623661"
+ id="rect12"
+ width="3.175"
+ height="1.8520833"
+ x="0.52916664"
+ y="294.61874"
+ ry="0.26457277" />
+ <path
+ style="fill:#000000;stroke-width:0.49999997;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:0.99999999, 2.99999996"
+ d="M 8 2 A 3.9999996 3.9999996 0 0 0 4 6 L 4 9 L 5.3613281 9 A 3.9999996 3.9999996 0 0 0 8 10 A 3.9999996 3.9999996 0 0 0 10.638672 9 L 12 9 L 12 6 A 3.9999996 3.9999996 0 0 0 8 2 z M 8 4 A 1.9999999 1.9999999 0 0 1 10 6 L 10 9 L 6 9 L 6 6 A 1.9999999 1.9999999 0 0 1 8 4 z "
+ transform="matrix(0.26458333,0,0,0.26458333,0,292.76665)"
+ id="path913" />
+ </g>
+</svg>
diff --git a/media/secure.xpm b/media/secure.xpm
new file mode 100644
index 0000000..49a3791
--- /dev/null
+++ b/media/secure.xpm
@@ -0,0 +1,56 @@
+/* XPM */
+static char *secure[] = {
+/* columns rows colors chars-per-pixel */
+"15 15 35 1 ",
+" c black",
+". c #000200",
+"X c #000500",
+"o c #000A00",
+"O c #000C00",
+"+ c #000D00",
+"@ c #001000",
+"# c #001100",
+"$ c #001400",
+"% c #003300",
+"& c #005500",
+"* c #005600",
+"= c #006600",
+"- c #007D00",
+"; c #007E00",
+": c #008700",
+"> c #008800",
+", c #00B300",
+"< c #00B400",
+"1 c #00C400",
+"2 c #00DB00",
+"3 c #00DC00",
+"4 c #00DD00",
+"5 c #00DF00",
+"6 c #00E500",
+"7 c #00E600",
+"8 c #00EA00",
+"9 c #00EB00",
+"0 c #00ED00",
+"q c #00F000",
+"w c #00F400",
+"e c #00F900",
+"r c #00FA00",
+"t c #00FC00",
+"y c green",
+/* pixels */
+"yyyyyyyyyyyyyyy",
+"yyyyyye6ryyyyyy",
+"yyyyt:O +>tyyyy",
+"yyyy- o%o ;yyyy",
+"yyy9X#3y2@X0yyy",
+"yyy1 *yyy& 1yyy",
+"yy<= %>>>% =,yy",
+"y8. 7y",
+"y5 5y",
+"y5 5y",
+"y5 5y",
+"y5 5y",
+"yw# $wy",
+"yyq444444444wyy",
+"yyyyyyyyyyyyyyy"
+};
diff --git a/po/CMakeLists.txt b/po/CMakeLists.txt
index e2382b8..ddc43de 100644
--- a/po/CMakeLists.txt
+++ b/po/CMakeLists.txt
@@ -1,8 +1,6 @@
# Gettext support - mostly borrowed from the Licq project
-set(po_FILES
- bg da de el eo es fi fr fur hu it nl pl pt_BR ru sk sr sv tr uk vi zh_CN
-)
+file(STRINGS LINGUAS po_FILES)
if (NOT GETTEXT_MSGMERGE_EXECUTABLE AND NOT GETTEXT_MSGFMT_EXECUTABLE)
message(FATAL_ERROR "Gettext message catalog tools NOT found")
@@ -15,6 +13,7 @@
RELATIVE ${PROJECT_SOURCE_DIR}
${PROJECT_SOURCE_DIR}/vncviewer/*.h
${PROJECT_SOURCE_DIR}/vncviewer/*.cxx
+ ${PROJECT_SOURCE_DIR}/vncviewer/*.desktop.in.in
)
add_custom_target(translations_update
diff --git a/po/LINGUAS b/po/LINGUAS
new file mode 100644
index 0000000..e9427f9
--- /dev/null
+++ b/po/LINGUAS
@@ -0,0 +1,22 @@
+bg
+da
+de
+el
+eo
+es
+fi
+fr
+fur
+hu
+it
+nl
+pl
+pt_BR
+ru
+sk
+sr
+sv
+tr
+uk
+vi
+zh_CN
diff --git a/po/da.po b/po/da.po
index 2ab3881..1859f44 100644
--- a/po/da.po
+++ b/po/da.po
@@ -656,3 +656,7 @@
#, c-format
msgid "Listening on port %d"
msgstr "Lytter på port %d"
+
+#: vncviewer/vncviewer.desktop.in.in:6
+msgid "Connect to VNC server and display remote desktop"
+msgstr "Opret forbindelse til VNC-server og vis fjern-skrivebord"
diff --git a/po/fr.po b/po/fr.po
index 911406e..dfddbf0 100644
--- a/po/fr.po
+++ b/po/fr.po
@@ -662,3 +662,7 @@
#, c-format
msgid "Listening on port %d"
msgstr "Écoute du port %d"
+
+#: vncviewer/vncviewer.desktop.in.in:6
+msgid "Connect to VNC server and display remote desktop"
+msgstr "Se connecter à un serveur VNC et afficher le bureau distant"
diff --git a/po/tigervnc.pot b/po/tigervnc.pot
index 0643650..70656bc 100644
--- a/po/tigervnc.pot
+++ b/po/tigervnc.pot
@@ -651,3 +651,7 @@
#, c-format
msgid "Listening on port %d"
msgstr ""
+
+#: vncviewer/vncviewer.desktop.in.in:6
+msgid "Connect to VNC server and display remote desktop"
+msgstr ""
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 60edb01..7e00681 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -40,5 +40,7 @@
target_link_libraries(fbperf msimg32)
endif()
if(APPLE)
- target_link_libraries(fbperf "-framework Cocoa" "-framework Carbon")
+ target_link_libraries(fbperf "-framework Cocoa")
+ target_link_libraries(fbperf "-framework Carbon")
+ target_link_libraries(fbperf "-framework IOKit")
endif()
diff --git a/unix/x0vncserver/CMakeLists.txt b/unix/x0vncserver/CMakeLists.txt
index 82f0d2a..7208f31 100644
--- a/unix/x0vncserver/CMakeLists.txt
+++ b/unix/x0vncserver/CMakeLists.txt
@@ -10,6 +10,8 @@
PollingManager.cxx
PollingScheduler.cxx
TimeMillis.cxx
+ qnum_to_xorgevdev.c
+ qnum_to_xorgkbd.c
x0vncserver.cxx
XPixelBuffer.cxx
../vncconfig/QueryConnectDialog.cxx
diff --git a/unix/x0vncserver/qnum_to_xorgevdev.c b/unix/x0vncserver/qnum_to_xorgevdev.c
new file mode 100644
index 0000000..357c88d
--- /dev/null
+++ b/unix/x0vncserver/qnum_to_xorgevdev.c
@@ -0,0 +1,245 @@
+/*
+ * This file is auto-generated from keymaps.csv on 2017-08-28 13:03
+ * Database checksum sha256(f8aeff0c3430077a350e3d7ba2b335b381bd929ac4b193413730a402ff3f0097)
+ * To re-generate, run:
+ * keymap-gen --lang=stdc code-map keymaps.csv qnum xorgevdev
+*/
+const unsigned short code_map_qnum_to_xorgevdev[254] = {
+ [0x1] = 0x9, /* qnum:1 -> linux:1 (KEY_ESC) -> xorgevdev:9 */
+ [0x2] = 0xa, /* qnum:2 -> linux:2 (KEY_1) -> xorgevdev:10 */
+ [0x3] = 0xb, /* qnum:3 -> linux:3 (KEY_2) -> xorgevdev:11 */
+ [0x4] = 0xc, /* qnum:4 -> linux:4 (KEY_3) -> xorgevdev:12 */
+ [0x5] = 0xd, /* qnum:5 -> linux:5 (KEY_4) -> xorgevdev:13 */
+ [0x6] = 0xe, /* qnum:6 -> linux:6 (KEY_5) -> xorgevdev:14 */
+ [0x7] = 0xf, /* qnum:7 -> linux:7 (KEY_6) -> xorgevdev:15 */
+ [0x8] = 0x10, /* qnum:8 -> linux:8 (KEY_7) -> xorgevdev:16 */
+ [0x9] = 0x11, /* qnum:9 -> linux:9 (KEY_8) -> xorgevdev:17 */
+ [0xa] = 0x12, /* qnum:10 -> linux:10 (KEY_9) -> xorgevdev:18 */
+ [0xb] = 0x13, /* qnum:11 -> linux:11 (KEY_0) -> xorgevdev:19 */
+ [0xc] = 0x14, /* qnum:12 -> linux:12 (KEY_MINUS) -> xorgevdev:20 */
+ [0xd] = 0x15, /* qnum:13 -> linux:13 (KEY_EQUAL) -> xorgevdev:21 */
+ [0xe] = 0x16, /* qnum:14 -> linux:14 (KEY_BACKSPACE) -> xorgevdev:22 */
+ [0xf] = 0x17, /* qnum:15 -> linux:15 (KEY_TAB) -> xorgevdev:23 */
+ [0x10] = 0x18, /* qnum:16 -> linux:16 (KEY_Q) -> xorgevdev:24 */
+ [0x11] = 0x19, /* qnum:17 -> linux:17 (KEY_W) -> xorgevdev:25 */
+ [0x12] = 0x1a, /* qnum:18 -> linux:18 (KEY_E) -> xorgevdev:26 */
+ [0x13] = 0x1b, /* qnum:19 -> linux:19 (KEY_R) -> xorgevdev:27 */
+ [0x14] = 0x1c, /* qnum:20 -> linux:20 (KEY_T) -> xorgevdev:28 */
+ [0x15] = 0x1d, /* qnum:21 -> linux:21 (KEY_Y) -> xorgevdev:29 */
+ [0x16] = 0x1e, /* qnum:22 -> linux:22 (KEY_U) -> xorgevdev:30 */
+ [0x17] = 0x1f, /* qnum:23 -> linux:23 (KEY_I) -> xorgevdev:31 */
+ [0x18] = 0x20, /* qnum:24 -> linux:24 (KEY_O) -> xorgevdev:32 */
+ [0x19] = 0x21, /* qnum:25 -> linux:25 (KEY_P) -> xorgevdev:33 */
+ [0x1a] = 0x22, /* qnum:26 -> linux:26 (KEY_LEFTBRACE) -> xorgevdev:34 */
+ [0x1b] = 0x23, /* qnum:27 -> linux:27 (KEY_RIGHTBRACE) -> xorgevdev:35 */
+ [0x1c] = 0x24, /* qnum:28 -> linux:28 (KEY_ENTER) -> xorgevdev:36 */
+ [0x1d] = 0x25, /* qnum:29 -> linux:29 (KEY_LEFTCTRL) -> xorgevdev:37 */
+ [0x1e] = 0x26, /* qnum:30 -> linux:30 (KEY_A) -> xorgevdev:38 */
+ [0x1f] = 0x27, /* qnum:31 -> linux:31 (KEY_S) -> xorgevdev:39 */
+ [0x20] = 0x28, /* qnum:32 -> linux:32 (KEY_D) -> xorgevdev:40 */
+ [0x21] = 0x29, /* qnum:33 -> linux:33 (KEY_F) -> xorgevdev:41 */
+ [0x22] = 0x2a, /* qnum:34 -> linux:34 (KEY_G) -> xorgevdev:42 */
+ [0x23] = 0x2b, /* qnum:35 -> linux:35 (KEY_H) -> xorgevdev:43 */
+ [0x24] = 0x2c, /* qnum:36 -> linux:36 (KEY_J) -> xorgevdev:44 */
+ [0x25] = 0x2d, /* qnum:37 -> linux:37 (KEY_K) -> xorgevdev:45 */
+ [0x26] = 0x2e, /* qnum:38 -> linux:38 (KEY_L) -> xorgevdev:46 */
+ [0x27] = 0x2f, /* qnum:39 -> linux:39 (KEY_SEMICOLON) -> xorgevdev:47 */
+ [0x28] = 0x30, /* qnum:40 -> linux:40 (KEY_APOSTROPHE) -> xorgevdev:48 */
+ [0x29] = 0x31, /* qnum:41 -> linux:41 (KEY_GRAVE) -> xorgevdev:49 */
+ [0x2a] = 0x32, /* qnum:42 -> linux:42 (KEY_LEFTSHIFT) -> xorgevdev:50 */
+ [0x2b] = 0x33, /* qnum:43 -> linux:43 (KEY_BACKSLASH) -> xorgevdev:51 */
+ [0x2c] = 0x34, /* qnum:44 -> linux:44 (KEY_Z) -> xorgevdev:52 */
+ [0x2d] = 0x35, /* qnum:45 -> linux:45 (KEY_X) -> xorgevdev:53 */
+ [0x2e] = 0x36, /* qnum:46 -> linux:46 (KEY_C) -> xorgevdev:54 */
+ [0x2f] = 0x37, /* qnum:47 -> linux:47 (KEY_V) -> xorgevdev:55 */
+ [0x30] = 0x38, /* qnum:48 -> linux:48 (KEY_B) -> xorgevdev:56 */
+ [0x31] = 0x39, /* qnum:49 -> linux:49 (KEY_N) -> xorgevdev:57 */
+ [0x32] = 0x3a, /* qnum:50 -> linux:50 (KEY_M) -> xorgevdev:58 */
+ [0x33] = 0x3b, /* qnum:51 -> linux:51 (KEY_COMMA) -> xorgevdev:59 */
+ [0x34] = 0x3c, /* qnum:52 -> linux:52 (KEY_DOT) -> xorgevdev:60 */
+ [0x35] = 0x3d, /* qnum:53 -> linux:53 (KEY_SLASH) -> xorgevdev:61 */
+ [0x36] = 0x3e, /* qnum:54 -> linux:54 (KEY_RIGHTSHIFT) -> xorgevdev:62 */
+ [0x37] = 0x3f, /* qnum:55 -> linux:55 (KEY_KPASTERISK) -> xorgevdev:63 */
+ [0x38] = 0x40, /* qnum:56 -> linux:56 (KEY_LEFTALT) -> xorgevdev:64 */
+ [0x39] = 0x41, /* qnum:57 -> linux:57 (KEY_SPACE) -> xorgevdev:65 */
+ [0x3a] = 0x42, /* qnum:58 -> linux:58 (KEY_CAPSLOCK) -> xorgevdev:66 */
+ [0x3b] = 0x43, /* qnum:59 -> linux:59 (KEY_F1) -> xorgevdev:67 */
+ [0x3c] = 0x44, /* qnum:60 -> linux:60 (KEY_F2) -> xorgevdev:68 */
+ [0x3d] = 0x45, /* qnum:61 -> linux:61 (KEY_F3) -> xorgevdev:69 */
+ [0x3e] = 0x46, /* qnum:62 -> linux:62 (KEY_F4) -> xorgevdev:70 */
+ [0x3f] = 0x47, /* qnum:63 -> linux:63 (KEY_F5) -> xorgevdev:71 */
+ [0x40] = 0x48, /* qnum:64 -> linux:64 (KEY_F6) -> xorgevdev:72 */
+ [0x41] = 0x49, /* qnum:65 -> linux:65 (KEY_F7) -> xorgevdev:73 */
+ [0x42] = 0x4a, /* qnum:66 -> linux:66 (KEY_F8) -> xorgevdev:74 */
+ [0x43] = 0x4b, /* qnum:67 -> linux:67 (KEY_F9) -> xorgevdev:75 */
+ [0x44] = 0x4c, /* qnum:68 -> linux:68 (KEY_F10) -> xorgevdev:76 */
+ [0x45] = 0x4d, /* qnum:69 -> linux:69 (KEY_NUMLOCK) -> xorgevdev:77 */
+ [0x46] = 0x4e, /* qnum:70 -> linux:70 (KEY_SCROLLLOCK) -> xorgevdev:78 */
+ [0x47] = 0x4f, /* qnum:71 -> linux:71 (KEY_KP7) -> xorgevdev:79 */
+ [0x48] = 0x50, /* qnum:72 -> linux:72 (KEY_KP8) -> xorgevdev:80 */
+ [0x49] = 0x51, /* qnum:73 -> linux:73 (KEY_KP9) -> xorgevdev:81 */
+ [0x4a] = 0x52, /* qnum:74 -> linux:74 (KEY_KPMINUS) -> xorgevdev:82 */
+ [0x4b] = 0x53, /* qnum:75 -> linux:75 (KEY_KP4) -> xorgevdev:83 */
+ [0x4c] = 0x54, /* qnum:76 -> linux:76 (KEY_KP5) -> xorgevdev:84 */
+ [0x4d] = 0x55, /* qnum:77 -> linux:77 (KEY_KP6) -> xorgevdev:85 */
+ [0x4e] = 0x56, /* qnum:78 -> linux:78 (KEY_KPPLUS) -> xorgevdev:86 */
+ [0x4f] = 0x57, /* qnum:79 -> linux:79 (KEY_KP1) -> xorgevdev:87 */
+ [0x50] = 0x58, /* qnum:80 -> linux:80 (KEY_KP2) -> xorgevdev:88 */
+ [0x51] = 0x59, /* qnum:81 -> linux:81 (KEY_KP3) -> xorgevdev:89 */
+ [0x52] = 0x5a, /* qnum:82 -> linux:82 (KEY_KP0) -> xorgevdev:90 */
+ [0x53] = 0x5b, /* qnum:83 -> linux:83 (KEY_KPDOT) -> xorgevdev:91 */
+ [0x54] = 0x6b, /* qnum:84 -> linux:99 (KEY_SYSRQ) -> xorgevdev:107 */
+ [0x55] = 0xc2, /* qnum:85 -> linux:186 (KEY_F16) -> xorgevdev:194 */
+ [0x56] = 0x5e, /* qnum:86 -> linux:86 (KEY_102ND) -> xorgevdev:94 */
+ [0x57] = 0x5f, /* qnum:87 -> linux:87 (KEY_F11) -> xorgevdev:95 */
+ [0x58] = 0x60, /* qnum:88 -> linux:88 (KEY_F12) -> xorgevdev:96 */
+ [0x59] = 0x7d, /* qnum:89 -> linux:117 (KEY_KPEQUAL) -> xorgevdev:125 */
+ [0x5a] = 0xc6, /* qnum:90 -> linux:190 (KEY_F20) -> xorgevdev:198 */
+ [0x5b] = 0x6d, /* qnum:91 -> linux:101 (KEY_LINEFEED) -> xorgevdev:109 */
+ [0x5c] = 0x67, /* qnum:92 -> linux:95 (KEY_KPJPCOMMA) -> xorgevdev:103 */
+ [0x5d] = 0xbf, /* qnum:93 -> linux:183 (KEY_F13) -> xorgevdev:191 */
+ [0x5e] = 0xc0, /* qnum:94 -> linux:184 (KEY_F14) -> xorgevdev:192 */
+ [0x5f] = 0xc1, /* qnum:95 -> linux:185 (KEY_F15) -> xorgevdev:193 */
+ [0x63] = 0xb1, /* qnum:99 -> linux:169 (KEY_PHONE) -> xorgevdev:177 */
+ [0x64] = 0x8e, /* qnum:100 -> linux:134 (KEY_OPEN) -> xorgevdev:142 */
+ [0x65] = 0x8f, /* qnum:101 -> linux:135 (KEY_PASTE) -> xorgevdev:143 */
+ [0x66] = 0x95, /* qnum:102 -> linux:141 (KEY_SETUP) -> xorgevdev:149 */
+ [0x67] = 0x98, /* qnum:103 -> linux:144 (KEY_FILE) -> xorgevdev:152 */
+ [0x68] = 0x99, /* qnum:104 -> linux:145 (KEY_SENDFILE) -> xorgevdev:153 */
+ [0x69] = 0x9a, /* qnum:105 -> linux:146 (KEY_DELETEFILE) -> xorgevdev:154 */
+ [0x6a] = 0x9f, /* qnum:106 -> linux:151 (KEY_MSDOS) -> xorgevdev:159 */
+ [0x6b] = 0xa1, /* qnum:107 -> linux:153 (KEY_DIRECTION) -> xorgevdev:161 */
+ [0x6c] = 0xa9, /* qnum:108 -> linux:161 (KEY_EJECTCD) -> xorgevdev:169 */
+ [0x6d] = 0xc9, /* qnum:109 -> linux:193 (KEY_F23) -> xorgevdev:201 */
+ [0x6f] = 0xca, /* qnum:111 -> linux:194 (KEY_F24) -> xorgevdev:202 */
+ [0x70] = 0xb2, /* qnum:112 -> linux:170 (KEY_ISO) -> xorgevdev:178 */
+ [0x71] = 0xb6, /* qnum:113 -> linux:174 (KEY_EXIT) -> xorgevdev:182 */
+ [0x72] = 0xb7, /* qnum:114 -> linux:175 (KEY_MOVE) -> xorgevdev:183 */
+ [0x73] = 0x61, /* qnum:115 -> linux:89 (KEY_RO) -> xorgevdev:97 */
+ [0x74] = 0xc7, /* qnum:116 -> linux:191 (KEY_F21) -> xorgevdev:199 */
+ [0x75] = 0xb9, /* qnum:117 -> linux:177 (KEY_SCROLLUP) -> xorgevdev:185 */
+ [0x76] = 0x5d, /* qnum:118 -> linux:85 (KEY_ZENKAKUHANKAKU) -> xorgevdev:93 */
+ [0x77] = 0x63, /* qnum:119 -> linux:91 (KEY_HIRAGANA) -> xorgevdev:99 */
+ [0x78] = 0x62, /* qnum:120 -> linux:90 (KEY_KATAKANA) -> xorgevdev:98 */
+ [0x79] = 0x64, /* qnum:121 -> linux:92 (KEY_HENKAN) -> xorgevdev:100 */
+ [0x7b] = 0x66, /* qnum:123 -> linux:94 (KEY_MUHENKAN) -> xorgevdev:102 */
+ [0x7d] = 0x84, /* qnum:125 -> linux:124 (KEY_YEN) -> xorgevdev:132 */
+ [0x7e] = 0x81, /* qnum:126 -> linux:121 (KEY_KPCOMMA) -> xorgevdev:129 */
+ [0x81] = 0xb3, /* qnum:129 -> linux:171 (KEY_CONFIG) -> xorgevdev:179 */
+ [0x82] = 0x9e, /* qnum:130 -> linux:150 (KEY_WWW) -> xorgevdev:158 */
+ [0x83] = 0xc3, /* qnum:131 -> linux:187 (KEY_F17) -> xorgevdev:195 */
+ [0x84] = 0xc5, /* qnum:132 -> linux:189 (KEY_F19) -> xorgevdev:197 */
+ [0x85] = 0x89, /* qnum:133 -> linux:129 (KEY_AGAIN) -> xorgevdev:137 */
+ [0x86] = 0x8a, /* qnum:134 -> linux:130 (KEY_PROPS) -> xorgevdev:138 */
+ [0x87] = 0x8b, /* qnum:135 -> linux:131 (KEY_UNDO) -> xorgevdev:139 */
+ [0x88] = 0xb8, /* qnum:136 -> linux:176 (KEY_EDIT) -> xorgevdev:184 */
+ [0x89] = 0xbd, /* qnum:137 -> linux:181 (KEY_NEW) -> xorgevdev:189 */
+ [0x8a] = 0xbe, /* qnum:138 -> linux:182 (KEY_REDO) -> xorgevdev:190 */
+ [0x8b] = 0x80, /* qnum:139 -> linux:120 (KEY_SCALE) -> xorgevdev:128 */
+ [0x8c] = 0x8c, /* qnum:140 -> linux:132 (KEY_FRONT) -> xorgevdev:140 */
+ [0x8d] = 0x83, /* qnum:141 -> linux:123 (KEY_HANJA) -> xorgevdev:131 */
+ [0x8e] = 0xf1, /* qnum:142 -> linux:233 (KEY_FORWARDMAIL) -> xorgevdev:241 */
+ [0x8f] = 0xba, /* qnum:143 -> linux:178 (KEY_SCROLLDOWN) -> xorgevdev:186 */
+ [0x90] = 0xad, /* qnum:144 -> linux:165 (KEY_PREVIOUSSONG) -> xorgevdev:173 */
+ [0x92] = 0xa0, /* qnum:146 -> linux:152 (KEY_SCREENLOCK) -> xorgevdev:160 */
+ [0x93] = 0x9b, /* qnum:147 -> linux:147 (KEY_XFER) -> xorgevdev:155 */
+ [0x94] = 0xe6, /* qnum:148 -> linux:222 (KEY_ALTERASE) -> xorgevdev:230 */
+ [0x95] = 0xcb, /* qnum:149 -> linux:195 (unnamed) -> xorgevdev:203 */
+ [0x96] = 0xcc, /* qnum:150 -> linux:196 (unnamed) -> xorgevdev:204 */
+ [0x97] = 0x9d, /* qnum:151 -> linux:149 (KEY_PROG2) -> xorgevdev:157 */
+ [0x98] = 0xb0, /* qnum:152 -> linux:168 (KEY_REWIND) -> xorgevdev:176 */
+ [0x99] = 0xab, /* qnum:153 -> linux:163 (KEY_NEXTSONG) -> xorgevdev:171 */
+ [0x9a] = 0xcd, /* qnum:154 -> linux:197 (unnamed) -> xorgevdev:205 */
+ [0x9b] = 0xce, /* qnum:155 -> linux:198 (unnamed) -> xorgevdev:206 */
+ [0x9c] = 0x68, /* qnum:156 -> linux:96 (KEY_KPENTER) -> xorgevdev:104 */
+ [0x9d] = 0x69, /* qnum:157 -> linux:97 (KEY_RIGHTCTRL) -> xorgevdev:105 */
+ [0x9e] = 0x93, /* qnum:158 -> linux:139 (KEY_MENU) -> xorgevdev:147 */
+ [0x9f] = 0x9c, /* qnum:159 -> linux:148 (KEY_PROG1) -> xorgevdev:156 */
+ [0xa0] = 0x79, /* qnum:160 -> linux:113 (KEY_MUTE) -> xorgevdev:121 */
+ [0xa1] = 0x94, /* qnum:161 -> linux:140 (KEY_CALC) -> xorgevdev:148 */
+ [0xa2] = 0xac, /* qnum:162 -> linux:164 (KEY_PLAYPAUSE) -> xorgevdev:172 */
+ [0xa3] = 0xa8, /* qnum:163 -> linux:160 (KEY_CLOSECD) -> xorgevdev:168 */
+ [0xa4] = 0xae, /* qnum:164 -> linux:166 (KEY_STOPCD) -> xorgevdev:174 */
+ [0xa5] = 0xd5, /* qnum:165 -> linux:205 (KEY_SUSPEND) -> xorgevdev:213 */
+ [0xa6] = 0xa2, /* qnum:166 -> linux:154 (KEY_CYCLEWINDOWS) -> xorgevdev:162 */
+ [0xa7] = 0xcf, /* qnum:167 -> linux:199 (unnamed) -> xorgevdev:207 */
+ [0xa8] = 0xd0, /* qnum:168 -> linux:200 (KEY_PLAYCD) -> xorgevdev:208 */
+ [0xa9] = 0xd1, /* qnum:169 -> linux:201 (KEY_PAUSECD) -> xorgevdev:209 */
+ [0xab] = 0xd2, /* qnum:171 -> linux:202 (KEY_PROG3) -> xorgevdev:210 */
+ [0xac] = 0xd3, /* qnum:172 -> linux:203 (KEY_PROG4) -> xorgevdev:211 */
+ [0xad] = 0xd4, /* qnum:173 -> linux:204 (KEY_DASHBOARD) -> xorgevdev:212 */
+ [0xae] = 0x7a, /* qnum:174 -> linux:114 (KEY_VOLUMEDOWN) -> xorgevdev:122 */
+ [0xaf] = 0xd6, /* qnum:175 -> linux:206 (KEY_CLOSE) -> xorgevdev:214 */
+ [0xb0] = 0x7b, /* qnum:176 -> linux:115 (KEY_VOLUMEUP) -> xorgevdev:123 */
+ [0xb1] = 0xaf, /* qnum:177 -> linux:167 (KEY_RECORD) -> xorgevdev:175 */
+ [0xb2] = 0xb4, /* qnum:178 -> linux:172 (KEY_HOMEPAGE) -> xorgevdev:180 */
+ [0xb3] = 0xd7, /* qnum:179 -> linux:207 (KEY_PLAY) -> xorgevdev:215 */
+ [0xb4] = 0xd8, /* qnum:180 -> linux:208 (KEY_FASTFORWARD) -> xorgevdev:216 */
+ [0xb5] = 0x6a, /* qnum:181 -> linux:98 (KEY_KPSLASH) -> xorgevdev:106 */
+ [0xb6] = 0xd9, /* qnum:182 -> linux:209 (KEY_BASSBOOST) -> xorgevdev:217 */
+ [0xb8] = 0x6c, /* qnum:184 -> linux:100 (KEY_RIGHTALT) -> xorgevdev:108 */
+ [0xb9] = 0xda, /* qnum:185 -> linux:210 (KEY_PRINT) -> xorgevdev:218 */
+ [0xba] = 0xdb, /* qnum:186 -> linux:211 (KEY_HP) -> xorgevdev:219 */
+ [0xbb] = 0xdc, /* qnum:187 -> linux:212 (KEY_CAMERA) -> xorgevdev:220 */
+ [0xbc] = 0x91, /* qnum:188 -> linux:137 (KEY_CUT) -> xorgevdev:145 */
+ [0xbd] = 0xdd, /* qnum:189 -> linux:213 (KEY_SOUND) -> xorgevdev:221 */
+ [0xbe] = 0xde, /* qnum:190 -> linux:214 (KEY_QUESTION) -> xorgevdev:222 */
+ [0xbf] = 0xdf, /* qnum:191 -> linux:215 (KEY_EMAIL) -> xorgevdev:223 */
+ [0xc0] = 0xe0, /* qnum:192 -> linux:216 (KEY_CHAT) -> xorgevdev:224 */
+ [0xc1] = 0x90, /* qnum:193 -> linux:136 (KEY_FIND) -> xorgevdev:144 */
+ [0xc2] = 0xe2, /* qnum:194 -> linux:218 (KEY_CONNECT) -> xorgevdev:226 */
+ [0xc3] = 0xe3, /* qnum:195 -> linux:219 (KEY_FINANCE) -> xorgevdev:227 */
+ [0xc4] = 0xe4, /* qnum:196 -> linux:220 (KEY_SPORT) -> xorgevdev:228 */
+ [0xc5] = 0xe5, /* qnum:197 -> linux:221 (KEY_SHOP) -> xorgevdev:229 */
+ [0xc6] = 0x7f, /* qnum:198 -> linux:119 (KEY_PAUSE) -> xorgevdev:127 */
+ [0xc7] = 0x6e, /* qnum:199 -> linux:102 (KEY_HOME) -> xorgevdev:110 */
+ [0xc8] = 0x6f, /* qnum:200 -> linux:103 (KEY_UP) -> xorgevdev:111 */
+ [0xc9] = 0x70, /* qnum:201 -> linux:104 (KEY_PAGEUP) -> xorgevdev:112 */
+ [0xca] = 0xe7, /* qnum:202 -> linux:223 (KEY_CANCEL) -> xorgevdev:231 */
+ [0xcb] = 0x71, /* qnum:203 -> linux:105 (KEY_LEFT) -> xorgevdev:113 */
+ [0xcc] = 0xe8, /* qnum:204 -> linux:224 (KEY_BRIGHTNESSDOWN) -> xorgevdev:232 */
+ [0xcd] = 0x72, /* qnum:205 -> linux:106 (KEY_RIGHT) -> xorgevdev:114 */
+ [0xce] = 0x7e, /* qnum:206 -> linux:118 (KEY_KPPLUSMINUS) -> xorgevdev:126 */
+ [0xcf] = 0x73, /* qnum:207 -> linux:107 (KEY_END) -> xorgevdev:115 */
+ [0xd0] = 0x74, /* qnum:208 -> linux:108 (KEY_DOWN) -> xorgevdev:116 */
+ [0xd1] = 0x75, /* qnum:209 -> linux:109 (KEY_PAGEDOWN) -> xorgevdev:117 */
+ [0xd2] = 0x76, /* qnum:210 -> linux:110 (KEY_INSERT) -> xorgevdev:118 */
+ [0xd3] = 0x77, /* qnum:211 -> linux:111 (KEY_DELETE) -> xorgevdev:119 */
+ [0xd4] = 0xe9, /* qnum:212 -> linux:225 (KEY_BRIGHTNESSUP) -> xorgevdev:233 */
+ [0xd5] = 0xf2, /* qnum:213 -> linux:234 (KEY_SAVE) -> xorgevdev:242 */
+ [0xd6] = 0xeb, /* qnum:214 -> linux:227 (KEY_SWITCHVIDEOMODE) -> xorgevdev:235 */
+ [0xd7] = 0xec, /* qnum:215 -> linux:228 (KEY_KBDILLUMTOGGLE) -> xorgevdev:236 */
+ [0xd8] = 0xed, /* qnum:216 -> linux:229 (KEY_KBDILLUMDOWN) -> xorgevdev:237 */
+ [0xd9] = 0xee, /* qnum:217 -> linux:230 (KEY_KBDILLUMUP) -> xorgevdev:238 */
+ [0xda] = 0xef, /* qnum:218 -> linux:231 (KEY_SEND) -> xorgevdev:239 */
+ [0xdb] = 0x85, /* qnum:219 -> linux:125 (KEY_LEFTMETA) -> xorgevdev:133 */
+ [0xdc] = 0x86, /* qnum:220 -> linux:126 (KEY_RIGHTMETA) -> xorgevdev:134 */
+ [0xdd] = 0x87, /* qnum:221 -> linux:127 (KEY_COMPOSE) -> xorgevdev:135 */
+ [0xde] = 0x7c, /* qnum:222 -> linux:116 (KEY_POWER) -> xorgevdev:124 */
+ [0xdf] = 0x96, /* qnum:223 -> linux:142 (KEY_SLEEP) -> xorgevdev:150 */
+ [0xe3] = 0x97, /* qnum:227 -> linux:143 (KEY_WAKEUP) -> xorgevdev:151 */
+ [0xe4] = 0xf0, /* qnum:228 -> linux:232 (KEY_REPLY) -> xorgevdev:240 */
+ [0xe5] = 0xe1, /* qnum:229 -> linux:217 (KEY_SEARCH) -> xorgevdev:225 */
+ [0xe6] = 0xa4, /* qnum:230 -> linux:156 (KEY_BOOKMARKS) -> xorgevdev:164 */
+ [0xe7] = 0xb5, /* qnum:231 -> linux:173 (KEY_REFRESH) -> xorgevdev:181 */
+ [0xe8] = 0x88, /* qnum:232 -> linux:128 (KEY_STOP) -> xorgevdev:136 */
+ [0xe9] = 0xa7, /* qnum:233 -> linux:159 (KEY_FORWARD) -> xorgevdev:167 */
+ [0xea] = 0xa6, /* qnum:234 -> linux:158 (KEY_BACK) -> xorgevdev:166 */
+ [0xeb] = 0xa5, /* qnum:235 -> linux:157 (KEY_COMPUTER) -> xorgevdev:165 */
+ [0xec] = 0xa3, /* qnum:236 -> linux:155 (KEY_MAIL) -> xorgevdev:163 */
+ [0xed] = 0xea, /* qnum:237 -> linux:226 (KEY_MEDIA) -> xorgevdev:234 */
+ [0xef] = 0x78, /* qnum:239 -> linux:112 (KEY_MACRO) -> xorgevdev:120 */
+ [0xf0] = 0xf3, /* qnum:240 -> linux:235 (KEY_DOCUMENTS) -> xorgevdev:243 */
+ [0xf1] = 0xf4, /* qnum:241 -> linux:236 (KEY_BATTERY) -> xorgevdev:244 */
+ [0xf2] = 0xf5, /* qnum:242 -> linux:237 (KEY_BLUETOOTH) -> xorgevdev:245 */
+ [0xf3] = 0xf6, /* qnum:243 -> linux:238 (KEY_WLAN) -> xorgevdev:246 */
+ [0xf4] = 0xf7, /* qnum:244 -> linux:239 (KEY_UWB) -> xorgevdev:247 */
+ [0xf5] = 0x92, /* qnum:245 -> linux:138 (KEY_HELP) -> xorgevdev:146 */
+ [0xf6] = 0xbb, /* qnum:246 -> linux:179 (KEY_KPLEFTPAREN) -> xorgevdev:187 */
+ [0xf7] = 0xc4, /* qnum:247 -> linux:188 (KEY_F18) -> xorgevdev:196 */
+ [0xf8] = 0x8d, /* qnum:248 -> linux:133 (KEY_COPY) -> xorgevdev:141 */
+ [0xf9] = 0xc8, /* qnum:249 -> linux:192 (KEY_F22) -> xorgevdev:200 */
+ [0xfb] = 0xbc, /* qnum:251 -> linux:180 (KEY_KPRIGHTPAREN) -> xorgevdev:188 */
+ [0xfd] = 0xaa, /* qnum:253 -> linux:162 (KEY_EJECTCLOSECD) -> xorgevdev:170 */
+};
+const unsigned int code_map_qnum_to_xorgevdev_len = sizeof(code_map_qnum_to_xorgevdev)/sizeof(code_map_qnum_to_xorgevdev[0]);
diff --git a/unix/x0vncserver/qnum_to_xorgkbd.c b/unix/x0vncserver/qnum_to_xorgkbd.c
new file mode 100644
index 0000000..57c2047
--- /dev/null
+++ b/unix/x0vncserver/qnum_to_xorgkbd.c
@@ -0,0 +1,121 @@
+/*
+ * This file is auto-generated from keymaps.csv on 2017-08-28 13:04
+ * Database checksum sha256(f8aeff0c3430077a350e3d7ba2b335b381bd929ac4b193413730a402ff3f0097)
+ * To re-generate, run:
+ * keymap-gen --lang=stdc code-map keymaps.csv qnum xorgkbd
+*/
+const unsigned short code_map_qnum_to_xorgkbd[254] = {
+ [0x1] = 0x9, /* qnum:1 -> linux:1 (KEY_ESC) -> xorgkbd:9 */
+ [0x2] = 0xa, /* qnum:2 -> linux:2 (KEY_1) -> xorgkbd:10 */
+ [0x3] = 0xb, /* qnum:3 -> linux:3 (KEY_2) -> xorgkbd:11 */
+ [0x4] = 0xc, /* qnum:4 -> linux:4 (KEY_3) -> xorgkbd:12 */
+ [0x5] = 0xd, /* qnum:5 -> linux:5 (KEY_4) -> xorgkbd:13 */
+ [0x6] = 0xe, /* qnum:6 -> linux:6 (KEY_5) -> xorgkbd:14 */
+ [0x7] = 0xf, /* qnum:7 -> linux:7 (KEY_6) -> xorgkbd:15 */
+ [0x8] = 0x10, /* qnum:8 -> linux:8 (KEY_7) -> xorgkbd:16 */
+ [0x9] = 0x11, /* qnum:9 -> linux:9 (KEY_8) -> xorgkbd:17 */
+ [0xa] = 0x12, /* qnum:10 -> linux:10 (KEY_9) -> xorgkbd:18 */
+ [0xb] = 0x13, /* qnum:11 -> linux:11 (KEY_0) -> xorgkbd:19 */
+ [0xc] = 0x14, /* qnum:12 -> linux:12 (KEY_MINUS) -> xorgkbd:20 */
+ [0xd] = 0x15, /* qnum:13 -> linux:13 (KEY_EQUAL) -> xorgkbd:21 */
+ [0xe] = 0x16, /* qnum:14 -> linux:14 (KEY_BACKSPACE) -> xorgkbd:22 */
+ [0xf] = 0x17, /* qnum:15 -> linux:15 (KEY_TAB) -> xorgkbd:23 */
+ [0x10] = 0x18, /* qnum:16 -> linux:16 (KEY_Q) -> xorgkbd:24 */
+ [0x11] = 0x19, /* qnum:17 -> linux:17 (KEY_W) -> xorgkbd:25 */
+ [0x12] = 0x1a, /* qnum:18 -> linux:18 (KEY_E) -> xorgkbd:26 */
+ [0x13] = 0x1b, /* qnum:19 -> linux:19 (KEY_R) -> xorgkbd:27 */
+ [0x14] = 0x1c, /* qnum:20 -> linux:20 (KEY_T) -> xorgkbd:28 */
+ [0x15] = 0x1d, /* qnum:21 -> linux:21 (KEY_Y) -> xorgkbd:29 */
+ [0x16] = 0x1e, /* qnum:22 -> linux:22 (KEY_U) -> xorgkbd:30 */
+ [0x17] = 0x1f, /* qnum:23 -> linux:23 (KEY_I) -> xorgkbd:31 */
+ [0x18] = 0x20, /* qnum:24 -> linux:24 (KEY_O) -> xorgkbd:32 */
+ [0x19] = 0x21, /* qnum:25 -> linux:25 (KEY_P) -> xorgkbd:33 */
+ [0x1a] = 0x22, /* qnum:26 -> linux:26 (KEY_LEFTBRACE) -> xorgkbd:34 */
+ [0x1b] = 0x23, /* qnum:27 -> linux:27 (KEY_RIGHTBRACE) -> xorgkbd:35 */
+ [0x1c] = 0x24, /* qnum:28 -> linux:28 (KEY_ENTER) -> xorgkbd:36 */
+ [0x1d] = 0x25, /* qnum:29 -> linux:29 (KEY_LEFTCTRL) -> xorgkbd:37 */
+ [0x1e] = 0x26, /* qnum:30 -> linux:30 (KEY_A) -> xorgkbd:38 */
+ [0x1f] = 0x27, /* qnum:31 -> linux:31 (KEY_S) -> xorgkbd:39 */
+ [0x20] = 0x28, /* qnum:32 -> linux:32 (KEY_D) -> xorgkbd:40 */
+ [0x21] = 0x29, /* qnum:33 -> linux:33 (KEY_F) -> xorgkbd:41 */
+ [0x22] = 0x2a, /* qnum:34 -> linux:34 (KEY_G) -> xorgkbd:42 */
+ [0x23] = 0x2b, /* qnum:35 -> linux:35 (KEY_H) -> xorgkbd:43 */
+ [0x24] = 0x2c, /* qnum:36 -> linux:36 (KEY_J) -> xorgkbd:44 */
+ [0x25] = 0x2d, /* qnum:37 -> linux:37 (KEY_K) -> xorgkbd:45 */
+ [0x26] = 0x2e, /* qnum:38 -> linux:38 (KEY_L) -> xorgkbd:46 */
+ [0x27] = 0x2f, /* qnum:39 -> linux:39 (KEY_SEMICOLON) -> xorgkbd:47 */
+ [0x28] = 0x30, /* qnum:40 -> linux:40 (KEY_APOSTROPHE) -> xorgkbd:48 */
+ [0x29] = 0x31, /* qnum:41 -> linux:41 (KEY_GRAVE) -> xorgkbd:49 */
+ [0x2a] = 0x32, /* qnum:42 -> linux:42 (KEY_LEFTSHIFT) -> xorgkbd:50 */
+ [0x2b] = 0x33, /* qnum:43 -> linux:43 (KEY_BACKSLASH) -> xorgkbd:51 */
+ [0x2c] = 0x34, /* qnum:44 -> linux:44 (KEY_Z) -> xorgkbd:52 */
+ [0x2d] = 0x35, /* qnum:45 -> linux:45 (KEY_X) -> xorgkbd:53 */
+ [0x2e] = 0x36, /* qnum:46 -> linux:46 (KEY_C) -> xorgkbd:54 */
+ [0x2f] = 0x37, /* qnum:47 -> linux:47 (KEY_V) -> xorgkbd:55 */
+ [0x30] = 0x38, /* qnum:48 -> linux:48 (KEY_B) -> xorgkbd:56 */
+ [0x31] = 0x39, /* qnum:49 -> linux:49 (KEY_N) -> xorgkbd:57 */
+ [0x32] = 0x3a, /* qnum:50 -> linux:50 (KEY_M) -> xorgkbd:58 */
+ [0x33] = 0x3b, /* qnum:51 -> linux:51 (KEY_COMMA) -> xorgkbd:59 */
+ [0x34] = 0x3c, /* qnum:52 -> linux:52 (KEY_DOT) -> xorgkbd:60 */
+ [0x35] = 0x3d, /* qnum:53 -> linux:53 (KEY_SLASH) -> xorgkbd:61 */
+ [0x36] = 0x3e, /* qnum:54 -> linux:54 (KEY_RIGHTSHIFT) -> xorgkbd:62 */
+ [0x37] = 0x3f, /* qnum:55 -> linux:55 (KEY_KPASTERISK) -> xorgkbd:63 */
+ [0x38] = 0x40, /* qnum:56 -> linux:56 (KEY_LEFTALT) -> xorgkbd:64 */
+ [0x39] = 0x41, /* qnum:57 -> linux:57 (KEY_SPACE) -> xorgkbd:65 */
+ [0x3a] = 0x42, /* qnum:58 -> linux:58 (KEY_CAPSLOCK) -> xorgkbd:66 */
+ [0x3b] = 0x43, /* qnum:59 -> linux:59 (KEY_F1) -> xorgkbd:67 */
+ [0x3c] = 0x44, /* qnum:60 -> linux:60 (KEY_F2) -> xorgkbd:68 */
+ [0x3d] = 0x45, /* qnum:61 -> linux:61 (KEY_F3) -> xorgkbd:69 */
+ [0x3e] = 0x46, /* qnum:62 -> linux:62 (KEY_F4) -> xorgkbd:70 */
+ [0x3f] = 0x47, /* qnum:63 -> linux:63 (KEY_F5) -> xorgkbd:71 */
+ [0x40] = 0x48, /* qnum:64 -> linux:64 (KEY_F6) -> xorgkbd:72 */
+ [0x41] = 0x49, /* qnum:65 -> linux:65 (KEY_F7) -> xorgkbd:73 */
+ [0x42] = 0x4a, /* qnum:66 -> linux:66 (KEY_F8) -> xorgkbd:74 */
+ [0x43] = 0x4b, /* qnum:67 -> linux:67 (KEY_F9) -> xorgkbd:75 */
+ [0x44] = 0x4c, /* qnum:68 -> linux:68 (KEY_F10) -> xorgkbd:76 */
+ [0x45] = 0x4d, /* qnum:69 -> linux:69 (KEY_NUMLOCK) -> xorgkbd:77 */
+ [0x46] = 0x4e, /* qnum:70 -> linux:70 (KEY_SCROLLLOCK) -> xorgkbd:78 */
+ [0x47] = 0x4f, /* qnum:71 -> linux:71 (KEY_KP7) -> xorgkbd:79 */
+ [0x48] = 0x50, /* qnum:72 -> linux:72 (KEY_KP8) -> xorgkbd:80 */
+ [0x49] = 0x51, /* qnum:73 -> linux:73 (KEY_KP9) -> xorgkbd:81 */
+ [0x4a] = 0x52, /* qnum:74 -> linux:74 (KEY_KPMINUS) -> xorgkbd:82 */
+ [0x4b] = 0x53, /* qnum:75 -> linux:75 (KEY_KP4) -> xorgkbd:83 */
+ [0x4c] = 0x54, /* qnum:76 -> linux:76 (KEY_KP5) -> xorgkbd:84 */
+ [0x4d] = 0x55, /* qnum:77 -> linux:77 (KEY_KP6) -> xorgkbd:85 */
+ [0x4e] = 0x56, /* qnum:78 -> linux:78 (KEY_KPPLUS) -> xorgkbd:86 */
+ [0x4f] = 0x57, /* qnum:79 -> linux:79 (KEY_KP1) -> xorgkbd:87 */
+ [0x50] = 0x58, /* qnum:80 -> linux:80 (KEY_KP2) -> xorgkbd:88 */
+ [0x51] = 0x59, /* qnum:81 -> linux:81 (KEY_KP3) -> xorgkbd:89 */
+ [0x52] = 0x5a, /* qnum:82 -> linux:82 (KEY_KP0) -> xorgkbd:90 */
+ [0x53] = 0x5b, /* qnum:83 -> linux:83 (KEY_KPDOT) -> xorgkbd:91 */
+ [0x54] = 0x6f, /* qnum:84 -> linux:99 (KEY_SYSRQ) -> xorgkbd:111 */
+ [0x55] = 0x79, /* qnum:85 -> linux:186 (KEY_F16) -> xorgkbd:121 */
+ [0x56] = 0x5e, /* qnum:86 -> linux:86 (KEY_102ND) -> xorgkbd:94 */
+ [0x57] = 0x5f, /* qnum:87 -> linux:87 (KEY_F11) -> xorgkbd:95 */
+ [0x58] = 0x60, /* qnum:88 -> linux:88 (KEY_F12) -> xorgkbd:96 */
+ [0x59] = 0x7e, /* qnum:89 -> linux:117 (KEY_KPEQUAL) -> xorgkbd:126 */
+ [0x5d] = 0x76, /* qnum:93 -> linux:183 (KEY_F13) -> xorgkbd:118 */
+ [0x5e] = 0x77, /* qnum:94 -> linux:184 (KEY_F14) -> xorgkbd:119 */
+ [0x5f] = 0x78, /* qnum:95 -> linux:185 (KEY_F15) -> xorgkbd:120 */
+ [0x7d] = 0x85, /* qnum:125 -> linux:124 (KEY_YEN) -> xorgkbd:133 */
+ [0x83] = 0x7a, /* qnum:131 -> linux:187 (KEY_F17) -> xorgkbd:122 */
+ [0x9c] = 0x6c, /* qnum:156 -> linux:96 (KEY_KPENTER) -> xorgkbd:108 */
+ [0x9d] = 0x6d, /* qnum:157 -> linux:97 (KEY_RIGHTCTRL) -> xorgkbd:109 */
+ [0xb5] = 0x70, /* qnum:181 -> linux:98 (KEY_KPSLASH) -> xorgkbd:112 */
+ [0xb8] = 0x71, /* qnum:184 -> linux:100 (KEY_RIGHTALT) -> xorgkbd:113 */
+ [0xc6] = 0x6e, /* qnum:198 -> linux:119 (KEY_PAUSE) -> xorgkbd:110 */
+ [0xc7] = 0x61, /* qnum:199 -> linux:102 (KEY_HOME) -> xorgkbd:97 */
+ [0xc8] = 0x62, /* qnum:200 -> linux:103 (KEY_UP) -> xorgkbd:98 */
+ [0xc9] = 0x63, /* qnum:201 -> linux:104 (KEY_PAGEUP) -> xorgkbd:99 */
+ [0xcb] = 0x64, /* qnum:203 -> linux:105 (KEY_LEFT) -> xorgkbd:100 */
+ [0xcd] = 0x66, /* qnum:205 -> linux:106 (KEY_RIGHT) -> xorgkbd:102 */
+ [0xcf] = 0x67, /* qnum:207 -> linux:107 (KEY_END) -> xorgkbd:103 */
+ [0xd0] = 0x68, /* qnum:208 -> linux:108 (KEY_DOWN) -> xorgkbd:104 */
+ [0xd1] = 0x69, /* qnum:209 -> linux:109 (KEY_PAGEDOWN) -> xorgkbd:105 */
+ [0xd2] = 0x6a, /* qnum:210 -> linux:110 (KEY_INSERT) -> xorgkbd:106 */
+ [0xd3] = 0x6b, /* qnum:211 -> linux:111 (KEY_DELETE) -> xorgkbd:107 */
+ [0xdb] = 0x73, /* qnum:219 -> linux:125 (KEY_LEFTMETA) -> xorgkbd:115 */
+ [0xdc] = 0x74, /* qnum:220 -> linux:126 (KEY_RIGHTMETA) -> xorgkbd:116 */
+ [0xdd] = 0x75, /* qnum:221 -> linux:127 (KEY_COMPOSE) -> xorgkbd:117 */
+};
+const unsigned int code_map_qnum_to_xorgkbd_len = sizeof(code_map_qnum_to_xorgkbd)/sizeof(code_map_qnum_to_xorgkbd[0]);
diff --git a/unix/x0vncserver/x0vncserver.cxx b/unix/x0vncserver/x0vncserver.cxx
index 8f73ac2..025a095 100644
--- a/unix/x0vncserver/x0vncserver.cxx
+++ b/unix/x0vncserver/x0vncserver.cxx
@@ -39,6 +39,7 @@
#include <X11/X.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
+#include <X11/XKBlib.h>
#ifdef HAVE_XTEST
#include <X11/extensions/XTest.h>
#endif
@@ -51,6 +52,12 @@
#include <x0vncserver/XPixelBuffer.h>
#include <x0vncserver/PollingScheduler.h>
+extern const unsigned short code_map_qnum_to_xorgevdev[];
+extern const unsigned int code_map_qnum_to_xorgevdev_len;
+
+extern const unsigned short code_map_qnum_to_xorgkbd[];
+extern const unsigned int code_map_qnum_to_xorgkbd_len;
+
// XXX Lynx/OS 2.3: protos for select(), bzero()
#ifdef Lynx
#include <sys/proto.h>
@@ -61,6 +68,14 @@
using namespace rfb;
using namespace network;
+// number of XKb indicator leds to handle
+static const int N_LEDS = 3;
+
+// order is important as it must match RFB extension
+static const char * ledNames[N_LEDS] = {
+ "Scroll Lock", "Num Lock", "Caps Lock"
+};
+
static LogWriter vlog("Main");
IntParameter pollingCycle("PollingCycle", "Milliseconds per one polling "
@@ -78,6 +93,10 @@
"rejecting the connection",
10);
StringParameter hostsFile("HostsFile", "File with IP access control rules", "");
+BoolParameter rawKeyboard("RawKeyboard",
+ "Send keyboard events straight through and "
+ "avoid mapping them to the current keyboard "
+ "layout", false);
//
// Allow the main loop terminate itself gracefully on receiving a signal.
@@ -141,12 +160,69 @@
XDesktop(Display* dpy_, Geometry *geometry_)
: dpy(dpy_), geometry(geometry_), pb(0), server(0),
oldButtonMask(0), haveXtest(false), haveDamage(false),
- maxButtons(0), running(false)
+ maxButtons(0), running(false), ledMasks(), ledState(0),
+ codeMap(0), codeMapLen(0)
{
+ int major, minor;
+
+ int xkbOpcode, xkbErrorBase;
+
+ major = XkbMajorVersion;
+ minor = XkbMinorVersion;
+ if (!XkbQueryExtension(dpy, &xkbOpcode, &xkbEventBase,
+ &xkbErrorBase, &major, &minor)) {
+ vlog.error("XKEYBOARD extension not present");
+ throw Exception();
+ }
+
+ XkbSelectEvents(dpy, XkbUseCoreKbd, XkbIndicatorStateNotifyMask,
+ XkbIndicatorStateNotifyMask);
+
+ // figure out bit masks for the indicators we are interested in
+ for (int i = 0; i < N_LEDS; i++) {
+ Atom a;
+ int shift;
+ Bool on;
+
+ a = XInternAtom(dpy, ledNames[i], True);
+ if (!a || !XkbGetNamedIndicator(dpy, a, &shift, &on, NULL, NULL))
+ continue;
+
+ ledMasks[i] = 1u << shift;
+ vlog.debug("Mask for '%s' is 0x%x", ledNames[i], ledMasks[i]);
+ if (on)
+ ledState |= 1u << i;
+ }
+
+ // X11 unfortunately uses keyboard driver specific keycodes and provides no
+ // direct way to query this, so guess based on the keyboard mapping
+ XkbDescPtr desc = XkbGetKeyboard(dpy, XkbAllComponentsMask, XkbUseCoreKbd);
+ if (desc && desc->names) {
+ char *keycodes = XGetAtomName(dpy, desc->names->keycodes);
+
+ if (keycodes) {
+ if (strncmp("evdev", keycodes, strlen("evdev")) == 0) {
+ codeMap = code_map_qnum_to_xorgevdev;
+ codeMapLen = code_map_qnum_to_xorgevdev_len;
+ vlog.info("Using evdev codemap\n");
+ } else if (strncmp("xfree86", keycodes, strlen("xfree86")) == 0) {
+ codeMap = code_map_qnum_to_xorgkbd;
+ codeMapLen = code_map_qnum_to_xorgkbd_len;
+ vlog.info("Using xorgkbd codemap\n");
+ } else {
+ vlog.info("Unknown keycode '%s', no codemap\n", keycodes);
+ }
+ XFree(keycodes);
+ } else {
+ vlog.debug("Unable to get keycode map\n");
+ }
+
+ XkbFreeKeyboard(desc, XkbAllComponentsMask, True);
+ }
+
#ifdef HAVE_XTEST
int xtestEventBase;
int xtestErrorBase;
- int major, minor;
if (XTestQueryExtension(dpy, &xtestEventBase,
&xtestErrorBase, &major, &minor)) {
@@ -165,7 +241,6 @@
int xdamageErrorBase;
if (XDamageQueryExtension(dpy, &xdamageEventBase, &xdamageErrorBase)) {
- TXWindow::setGlobalEventHandler(this);
haveDamage = true;
} else {
#endif
@@ -174,6 +249,8 @@
#ifdef HAVE_XDAMAGE
}
#endif
+
+ TXWindow::setGlobalEventHandler(this);
}
virtual ~XDesktop() {
stop();
@@ -212,6 +289,8 @@
}
#endif
+ server->setLEDState(ledState);
+
running = true;
}
@@ -253,12 +332,67 @@
#endif
}
- virtual void keyEvent(rdr::U32 key, bool down) {
#ifdef HAVE_XTEST
- if (!haveXtest) return;
- int keycode = XKeysymToKeycode(dpy, key);
- if (keycode)
- XTestFakeKeyEvent(dpy, keycode, down, CurrentTime);
+ KeyCode XkbKeysymToKeycode(Display* dpy, KeySym keysym) {
+ XkbDescPtr xkb;
+ XkbStateRec state;
+ unsigned keycode;
+
+ xkb = XkbGetMap(dpy, XkbAllComponentsMask, XkbUseCoreKbd);
+ if (!xkb)
+ return 0;
+
+ XkbGetState(dpy, XkbUseCoreKbd, &state);
+
+ for (keycode = xkb->min_key_code;
+ keycode <= xkb->max_key_code;
+ keycode++) {
+ KeySym cursym;
+ unsigned int mods;
+ XkbTranslateKeyCode(xkb, keycode, state.compat_state, &mods, &cursym);
+ if (cursym == keysym)
+ break;
+ }
+
+ if (keycode > xkb->max_key_code)
+ keycode = 0;
+
+ XkbFreeKeyboard(xkb, XkbAllComponentsMask, True);
+
+ return keycode;
+ }
+#endif
+
+ virtual void keyEvent(rdr::U32 keysym, rdr::U32 xtcode, bool down) {
+#ifdef HAVE_XTEST
+ int keycode = 0;
+
+ if (!haveXtest)
+ return;
+
+ // Use scan code if provided and mapping exists
+ if (codeMap && rawKeyboard && xtcode < codeMapLen)
+ keycode = codeMap[xtcode];
+
+ if (!keycode) {
+ if (!down || (pressedKeys.find(keysym) != pressedKeys.end()))
+ keycode = pressedKeys[keysym];
+ else {
+ // XKeysymToKeycode() doesn't respect state, so we have to use
+ // something slightly more complex
+ keycode = XkbKeysymToKeycode(dpy, keysym);
+ }
+ }
+
+ if (!keycode)
+ return;
+
+ if (down)
+ pressedKeys[keysym] = keycode;
+ else
+ pressedKeys.erase(keysym);
+
+ XTestFakeKeyEvent(dpy, keycode, down, CurrentTime);
#endif
}
@@ -272,24 +406,41 @@
// -=- TXGlobalEventHandler interface
virtual bool handleGlobalEvent(XEvent* ev) {
-#ifdef HAVE_XDAMAGE
- XDamageNotifyEvent* dev;
- Rect rect;
+ if (ev->type == xkbEventBase + XkbEventCode) {
+ XkbEvent *kb = (XkbEvent *)ev;
- if (ev->type != xdamageEventBase)
- return false;
+ if (kb->any.xkb_type != XkbIndicatorStateNotify)
+ return false;
- if (!running)
+ vlog.debug("Got indicator update, mask is now 0x%x", kb->indicators.state);
+
+ ledState = 0;
+ for (int i = 0; i < N_LEDS; i++) {
+ if (kb->indicators.state & ledMasks[i])
+ ledState |= 1u << i;
+ }
+
+ if (running)
+ server->setLEDState(ledState);
+
return true;
+#ifdef HAVE_XDAMAGE
+ } else if (ev->type == xdamageEventBase) {
+ XDamageNotifyEvent* dev;
+ Rect rect;
- dev = (XDamageNotifyEvent*)ev;
- rect.setXYWH(dev->area.x, dev->area.y, dev->area.width, dev->area.height);
- server->add_changed(rect);
+ if (!running)
+ return true;
- return true;
-#else
- return false;
+ dev = (XDamageNotifyEvent*)ev;
+ rect.setXYWH(dev->area.x, dev->area.y, dev->area.width, dev->area.height);
+ server->add_changed(rect);
+
+ return true;
#endif
+ }
+
+ return false;
}
protected:
@@ -301,11 +452,17 @@
bool haveXtest;
bool haveDamage;
int maxButtons;
+ std::map<KeySym, KeyCode> pressedKeys;
bool running;
#ifdef HAVE_XDAMAGE
Damage damage;
int xdamageEventBase;
#endif
+ int xkbEventBase;
+ int ledMasks[N_LEDS];
+ unsigned ledState;
+ const unsigned short *codeMap;
+ unsigned codeMapLen;
};
diff --git a/unix/x0vncserver/x0vncserver.man b/unix/x0vncserver/x0vncserver.man
index 095f6db..804a70f 100644
--- a/unix/x0vncserver/x0vncserver.man
+++ b/unix/x0vncserver/x0vncserver.man
@@ -168,6 +168,13 @@
`greater than' characters).
.
.TP
+.B RawKeyboard
+Send keyboard events straight through and avoid mapping them to the current
+keyboard layout. This effectively makes the keyboard behave according to the
+layout configured on the server instead of the layout configured on the
+client. Default is off.
+.
+.TP
.B Protocol3.3
Always use RFB protocol version 3.3 for backwards compatibility with
badly-behaved clients. Default is off.
diff --git a/unix/xserver/hw/vnc/Input.c b/unix/xserver/hw/vnc/Input.c
index 33e8604..a50f33a 100644
--- a/unix/xserver/hw/vnc/Input.c
+++ b/unix/xserver/hw/vnc/Input.c
@@ -44,6 +44,11 @@
#include <X11/Xlib.h>
#include <X11/Xutil.h>
+extern const unsigned short code_map_qnum_to_xorgevdev[];
+extern const unsigned int code_map_qnum_to_xorgevdev_len;
+extern const unsigned short code_map_qnum_to_xorgkbd[];
+extern const unsigned int code_map_qnum_to_xorgkbd_len;
+
#define BUTTONS 7
/* Event queue is shared between all devices. */
@@ -57,6 +62,9 @@
static int oldButtonMask;
static int cursorPosX, cursorPosY;
+static const unsigned short *codeMap;
+static unsigned int codeMapLen;
+
static KeySym pressedKeys[256];
static int vncPointerProc(DeviceIntPtr pDevice, int onoff);
@@ -64,6 +72,8 @@
void * ctrl, int class);
static int vncKeyboardProc(DeviceIntPtr pDevice, int onoff);
+static void vncKeysymKeyboardEvent(KeySym keysym, int down);
+
#define LOG_NAME "Input"
#define LOG_ERROR(...) vncLogError(LOG_NAME, __VA_ARGS__)
@@ -88,6 +98,19 @@
if ((vncPointerDev != NULL) || (vncKeyboardDev != NULL))
return;
+ /*
+ * On Linux we try to provide the same key codes as Xorg with
+ * the evdev driver. On other platforms we mimic the older
+ * Xorg KBD driver.
+ */
+#ifdef __linux__
+ codeMap = code_map_qnum_to_xorgevdev;
+ codeMapLen = code_map_qnum_to_xorgevdev_len;
+#else
+ codeMap = code_map_qnum_to_xorgkbd;
+ codeMapLen = code_map_qnum_to_xorgkbd_len;
+#endif
+
for (i = 0;i < 256;i++)
pressedKeys[i] = NoSymbol;
@@ -273,6 +296,11 @@
vncBell();
}
+static void vncKeyboardCtrl(DeviceIntPtr pDevice, KeybdCtrl *ctrl)
+{
+ vncSetLEDState(ctrl->leds);
+}
+
static int vncKeyboardProc(DeviceIntPtr pDevice, int onoff)
{
DevicePtr pDev = (DevicePtr)pDevice;
@@ -280,7 +308,7 @@
switch (onoff) {
case DEVICE_INIT:
InitKeyboardDeviceStruct(pDevice, NULL, vncKeyboardBell,
- (KbdCtrlProcPtr)NoopDDA);
+ vncKeyboardCtrl);
break;
case DEVICE_ON:
pDev->on = TRUE;
@@ -317,6 +345,48 @@
#endif
}
+/*
+ * vncKeyboardEvent() - add X11 events for the given RFB key event
+ */
+void vncKeyboardEvent(KeySym keysym, unsigned xtcode, int down)
+{
+ /* Simple case: the client has specified the key */
+ if (xtcode && xtcode < codeMapLen) {
+ int keycode;
+
+ keycode = codeMap[xtcode];
+ if (!keycode) {
+ /*
+ * Figure something out based on keysym if we
+ * cannot find a mapping.
+ */
+ if (keysym)
+ vncKeysymKeyboardEvent(keysym, down);
+ return;
+ }
+
+ /*
+ * We update the state table in case we get a mix of
+ * events with and without key codes.
+ */
+ if (down)
+ pressedKeys[keycode] = keysym;
+ else
+ pressedKeys[keycode] = NoSymbol;
+
+ pressKey(vncKeyboardDev, keycode, down, "raw keycode");
+ mieqProcessInputEvents();
+ return;
+ }
+
+ /*
+ * Advanced case: We have to figure out a sequence of keys that
+ * result in the given keysym
+ */
+ if (keysym)
+ vncKeysymKeyboardEvent(keysym, down);
+}
+
/* altKeysym is a table of alternative keysyms which have the same meaning. */
static struct altKeysym_t {
@@ -367,14 +437,14 @@
};
/*
- * vncKeyboardEvent() - work out the best keycode corresponding to the keysym
- * sent by the viewer. This is basically impossible in the general case, but
- * we make a best effort by assuming that all useful keysyms can be reached
- * using just the Shift and Level 3 (AltGr) modifiers. For core keyboards this
- * is basically always true, and should be true for most sane, western XKB
- * layouts.
+ * vncKeysymKeyboardEvent() - work out the best keycode corresponding
+ * to the keysym sent by the viewer. This is basically impossible in
+ * the general case, but we make a best effort by assuming that all
+ * useful keysyms can be reached using just the Shift and
+ * Level 3 (AltGr) modifiers. For core keyboards this is basically
+ * always true, and should be true for most sane, western XKB layouts.
*/
-void vncKeyboardEvent(KeySym keysym, int down)
+static void vncKeysymKeyboardEvent(KeySym keysym, int down)
{
int i;
unsigned state, new_state;
@@ -437,12 +507,6 @@
}
}
- /* We don't have lock synchronisation... */
- if (vncIsLockModifier(keycode, new_state)) {
- LOG_DEBUG("Ignoring lock key (e.g. caps lock)");
- return;
- }
-
/* No matches. Will have to add a new entry... */
if (keycode == 0) {
keycode = vncAddKeysym(keysym, state);
diff --git a/unix/xserver/hw/vnc/Input.h b/unix/xserver/hw/vnc/Input.h
index 11e8871..a9d067f 100644
--- a/unix/xserver/hw/vnc/Input.h
+++ b/unix/xserver/hw/vnc/Input.h
@@ -36,7 +36,7 @@
void vncPointerMove(int x, int y);
void vncGetPointerPos(int *x, int *y);
-void vncKeyboardEvent(KeySym keysym, int down);
+void vncKeyboardEvent(KeySym keysym, unsigned xtcode, int down);
/* Backend dependent functions below here */
@@ -53,8 +53,6 @@
KeyCode vncKeysymToKeycode(KeySym keysym, unsigned state, unsigned *new_state);
-int vncIsLockModifier(KeyCode keycode, unsigned state);
-
int vncIsAffectedByNumLock(KeyCode keycode);
KeyCode vncAddKeysym(KeySym keysym, unsigned state);
diff --git a/unix/xserver/hw/vnc/InputXKB.c b/unix/xserver/hw/vnc/InputXKB.c
index 2a3f7af..a9bd11d 100644
--- a/unix/xserver/hw/vnc/InputXKB.c
+++ b/unix/xserver/hw/vnc/InputXKB.c
@@ -485,23 +485,6 @@
return 0;
}
-int vncIsLockModifier(KeyCode keycode, unsigned state)
-{
- XkbDescPtr xkb;
- XkbAction *act;
-
- xkb = GetMaster(vncKeyboardDev, KEYBOARD_OR_FLOAT)->key->xkbInfo->desc;
-
- act = XkbKeyActionPtr(xkb, keycode, state);
- if (act == NULL)
- return 0;
-
- if (act->type != XkbSA_LockMods)
- return 0;
-
- return 1;
-}
-
int vncIsAffectedByNumLock(KeyCode keycode)
{
unsigned state;
diff --git a/unix/xserver/hw/vnc/Makefile.am b/unix/xserver/hw/vnc/Makefile.am
index 0d6a4ac..24a40b3 100644
--- a/unix/xserver/hw/vnc/Makefile.am
+++ b/unix/xserver/hw/vnc/Makefile.am
@@ -18,7 +18,7 @@
libvnccommon_la_SOURCES = $(HDRS) \
vncExt.c vncExtInit.cc vncHooks.c vncSelection.c \
vncBlockHandler.c XorgGlue.c RFBGlue.cc XserverDesktop.cc \
- Input.c InputXKB.c
+ Input.c InputXKB.c qnum_to_xorgevdev.c qnum_to_xorgkbd.c
libvnccommon_la_CPPFLAGS = -DVENDOR_RELEASE="$(VENDOR_RELEASE)" \
-DVENDOR_STRING="\"$(VENDOR_STRING)\"" -I$(TIGERVNC_SRCDIR)/common -UHAVE_CONFIG_H \
diff --git a/unix/xserver/hw/vnc/XserverDesktop.cc b/unix/xserver/hw/vnc/XserverDesktop.cc
index 4836782..2131db6 100644
--- a/unix/xserver/hw/vnc/XserverDesktop.cc
+++ b/unix/xserver/hw/vnc/XserverDesktop.cc
@@ -53,6 +53,11 @@
static LogWriter vlog("XserverDesktop");
+BoolParameter rawKeyboard("RawKeyboard",
+ "Send keyboard events straight through and "
+ "avoid mapping them to the current keyboard "
+ "layout", false);
+
class FileHTTPServer : public rfb::HTTPServer {
public:
FileHTTPServer(XserverDesktop* d) : desktop(d) {}
@@ -322,6 +327,11 @@
server->bell();
}
+void XserverDesktop::setLEDState(unsigned int state)
+{
+ server->setLEDState(state);
+}
+
void XserverDesktop::serverCutText(const char* str, int len)
{
try {
@@ -766,7 +776,10 @@
}
}
-void XserverDesktop::keyEvent(rdr::U32 keysym, bool down)
+void XserverDesktop::keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down)
{
- vncKeyboardEvent(keysym, down);
+ if (!rawKeyboard)
+ keycode = 0;
+
+ vncKeyboardEvent(keysym, keycode, down);
}
diff --git a/unix/xserver/hw/vnc/XserverDesktop.h b/unix/xserver/hw/vnc/XserverDesktop.h
index c766c26..7f7823a 100644
--- a/unix/xserver/hw/vnc/XserverDesktop.h
+++ b/unix/xserver/hw/vnc/XserverDesktop.h
@@ -63,6 +63,7 @@
void setFramebuffer(int w, int h, void* fbptr, int stride);
void refreshScreenLayout();
void bell();
+ void setLEDState(unsigned int state);
void serverCutText(const char* str, int len);
void setDesktopName(const char* name);
void setCursor(int width, int height, int hotX, int hotY,
@@ -88,7 +89,7 @@
// rfb::SDesktop callbacks
virtual void pointerEvent(const rfb::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 rfb::Point getFbSize() { return rfb::Point(width(), height()); }
virtual unsigned int setScreenLayout(int fb_width, int fb_height,
diff --git a/unix/xserver/hw/vnc/Xvnc.man b/unix/xserver/hw/vnc/Xvnc.man
index 04e8f94..36777c2 100644
--- a/unix/xserver/hw/vnc/Xvnc.man
+++ b/unix/xserver/hw/vnc/Xvnc.man
@@ -294,8 +294,10 @@
where
.I char
is a hexadecimal keysym. For example, to exchange the " and @ symbols you would specify the following:
-.IP "" 10
+
+.RS 10
RemapKeys=0x22<>0x40
+.RE
.
.TP
.B \-AvoidShiftNumLock
@@ -305,6 +307,13 @@
(e.g. a Return instead of a keypad Enter).
.
.TP
+.B \-RawKeyboard
+Send keyboard events straight through and avoid mapping them to the current
+keyboard layout. This effectively makes the keyboard behave according to the
+layout configured on the server instead of the layout configured on the
+client. Default is off.
+.
+.TP
.B \-AllowOverride
Comma separated list of parameters that can be modified using VNC extension.
Parameters can be modified for example using \fBvncconfig\fP(1) program from
diff --git a/unix/xserver/hw/vnc/qnum_to_xorgevdev.c b/unix/xserver/hw/vnc/qnum_to_xorgevdev.c
new file mode 100644
index 0000000..357c88d
--- /dev/null
+++ b/unix/xserver/hw/vnc/qnum_to_xorgevdev.c
@@ -0,0 +1,245 @@
+/*
+ * This file is auto-generated from keymaps.csv on 2017-08-28 13:03
+ * Database checksum sha256(f8aeff0c3430077a350e3d7ba2b335b381bd929ac4b193413730a402ff3f0097)
+ * To re-generate, run:
+ * keymap-gen --lang=stdc code-map keymaps.csv qnum xorgevdev
+*/
+const unsigned short code_map_qnum_to_xorgevdev[254] = {
+ [0x1] = 0x9, /* qnum:1 -> linux:1 (KEY_ESC) -> xorgevdev:9 */
+ [0x2] = 0xa, /* qnum:2 -> linux:2 (KEY_1) -> xorgevdev:10 */
+ [0x3] = 0xb, /* qnum:3 -> linux:3 (KEY_2) -> xorgevdev:11 */
+ [0x4] = 0xc, /* qnum:4 -> linux:4 (KEY_3) -> xorgevdev:12 */
+ [0x5] = 0xd, /* qnum:5 -> linux:5 (KEY_4) -> xorgevdev:13 */
+ [0x6] = 0xe, /* qnum:6 -> linux:6 (KEY_5) -> xorgevdev:14 */
+ [0x7] = 0xf, /* qnum:7 -> linux:7 (KEY_6) -> xorgevdev:15 */
+ [0x8] = 0x10, /* qnum:8 -> linux:8 (KEY_7) -> xorgevdev:16 */
+ [0x9] = 0x11, /* qnum:9 -> linux:9 (KEY_8) -> xorgevdev:17 */
+ [0xa] = 0x12, /* qnum:10 -> linux:10 (KEY_9) -> xorgevdev:18 */
+ [0xb] = 0x13, /* qnum:11 -> linux:11 (KEY_0) -> xorgevdev:19 */
+ [0xc] = 0x14, /* qnum:12 -> linux:12 (KEY_MINUS) -> xorgevdev:20 */
+ [0xd] = 0x15, /* qnum:13 -> linux:13 (KEY_EQUAL) -> xorgevdev:21 */
+ [0xe] = 0x16, /* qnum:14 -> linux:14 (KEY_BACKSPACE) -> xorgevdev:22 */
+ [0xf] = 0x17, /* qnum:15 -> linux:15 (KEY_TAB) -> xorgevdev:23 */
+ [0x10] = 0x18, /* qnum:16 -> linux:16 (KEY_Q) -> xorgevdev:24 */
+ [0x11] = 0x19, /* qnum:17 -> linux:17 (KEY_W) -> xorgevdev:25 */
+ [0x12] = 0x1a, /* qnum:18 -> linux:18 (KEY_E) -> xorgevdev:26 */
+ [0x13] = 0x1b, /* qnum:19 -> linux:19 (KEY_R) -> xorgevdev:27 */
+ [0x14] = 0x1c, /* qnum:20 -> linux:20 (KEY_T) -> xorgevdev:28 */
+ [0x15] = 0x1d, /* qnum:21 -> linux:21 (KEY_Y) -> xorgevdev:29 */
+ [0x16] = 0x1e, /* qnum:22 -> linux:22 (KEY_U) -> xorgevdev:30 */
+ [0x17] = 0x1f, /* qnum:23 -> linux:23 (KEY_I) -> xorgevdev:31 */
+ [0x18] = 0x20, /* qnum:24 -> linux:24 (KEY_O) -> xorgevdev:32 */
+ [0x19] = 0x21, /* qnum:25 -> linux:25 (KEY_P) -> xorgevdev:33 */
+ [0x1a] = 0x22, /* qnum:26 -> linux:26 (KEY_LEFTBRACE) -> xorgevdev:34 */
+ [0x1b] = 0x23, /* qnum:27 -> linux:27 (KEY_RIGHTBRACE) -> xorgevdev:35 */
+ [0x1c] = 0x24, /* qnum:28 -> linux:28 (KEY_ENTER) -> xorgevdev:36 */
+ [0x1d] = 0x25, /* qnum:29 -> linux:29 (KEY_LEFTCTRL) -> xorgevdev:37 */
+ [0x1e] = 0x26, /* qnum:30 -> linux:30 (KEY_A) -> xorgevdev:38 */
+ [0x1f] = 0x27, /* qnum:31 -> linux:31 (KEY_S) -> xorgevdev:39 */
+ [0x20] = 0x28, /* qnum:32 -> linux:32 (KEY_D) -> xorgevdev:40 */
+ [0x21] = 0x29, /* qnum:33 -> linux:33 (KEY_F) -> xorgevdev:41 */
+ [0x22] = 0x2a, /* qnum:34 -> linux:34 (KEY_G) -> xorgevdev:42 */
+ [0x23] = 0x2b, /* qnum:35 -> linux:35 (KEY_H) -> xorgevdev:43 */
+ [0x24] = 0x2c, /* qnum:36 -> linux:36 (KEY_J) -> xorgevdev:44 */
+ [0x25] = 0x2d, /* qnum:37 -> linux:37 (KEY_K) -> xorgevdev:45 */
+ [0x26] = 0x2e, /* qnum:38 -> linux:38 (KEY_L) -> xorgevdev:46 */
+ [0x27] = 0x2f, /* qnum:39 -> linux:39 (KEY_SEMICOLON) -> xorgevdev:47 */
+ [0x28] = 0x30, /* qnum:40 -> linux:40 (KEY_APOSTROPHE) -> xorgevdev:48 */
+ [0x29] = 0x31, /* qnum:41 -> linux:41 (KEY_GRAVE) -> xorgevdev:49 */
+ [0x2a] = 0x32, /* qnum:42 -> linux:42 (KEY_LEFTSHIFT) -> xorgevdev:50 */
+ [0x2b] = 0x33, /* qnum:43 -> linux:43 (KEY_BACKSLASH) -> xorgevdev:51 */
+ [0x2c] = 0x34, /* qnum:44 -> linux:44 (KEY_Z) -> xorgevdev:52 */
+ [0x2d] = 0x35, /* qnum:45 -> linux:45 (KEY_X) -> xorgevdev:53 */
+ [0x2e] = 0x36, /* qnum:46 -> linux:46 (KEY_C) -> xorgevdev:54 */
+ [0x2f] = 0x37, /* qnum:47 -> linux:47 (KEY_V) -> xorgevdev:55 */
+ [0x30] = 0x38, /* qnum:48 -> linux:48 (KEY_B) -> xorgevdev:56 */
+ [0x31] = 0x39, /* qnum:49 -> linux:49 (KEY_N) -> xorgevdev:57 */
+ [0x32] = 0x3a, /* qnum:50 -> linux:50 (KEY_M) -> xorgevdev:58 */
+ [0x33] = 0x3b, /* qnum:51 -> linux:51 (KEY_COMMA) -> xorgevdev:59 */
+ [0x34] = 0x3c, /* qnum:52 -> linux:52 (KEY_DOT) -> xorgevdev:60 */
+ [0x35] = 0x3d, /* qnum:53 -> linux:53 (KEY_SLASH) -> xorgevdev:61 */
+ [0x36] = 0x3e, /* qnum:54 -> linux:54 (KEY_RIGHTSHIFT) -> xorgevdev:62 */
+ [0x37] = 0x3f, /* qnum:55 -> linux:55 (KEY_KPASTERISK) -> xorgevdev:63 */
+ [0x38] = 0x40, /* qnum:56 -> linux:56 (KEY_LEFTALT) -> xorgevdev:64 */
+ [0x39] = 0x41, /* qnum:57 -> linux:57 (KEY_SPACE) -> xorgevdev:65 */
+ [0x3a] = 0x42, /* qnum:58 -> linux:58 (KEY_CAPSLOCK) -> xorgevdev:66 */
+ [0x3b] = 0x43, /* qnum:59 -> linux:59 (KEY_F1) -> xorgevdev:67 */
+ [0x3c] = 0x44, /* qnum:60 -> linux:60 (KEY_F2) -> xorgevdev:68 */
+ [0x3d] = 0x45, /* qnum:61 -> linux:61 (KEY_F3) -> xorgevdev:69 */
+ [0x3e] = 0x46, /* qnum:62 -> linux:62 (KEY_F4) -> xorgevdev:70 */
+ [0x3f] = 0x47, /* qnum:63 -> linux:63 (KEY_F5) -> xorgevdev:71 */
+ [0x40] = 0x48, /* qnum:64 -> linux:64 (KEY_F6) -> xorgevdev:72 */
+ [0x41] = 0x49, /* qnum:65 -> linux:65 (KEY_F7) -> xorgevdev:73 */
+ [0x42] = 0x4a, /* qnum:66 -> linux:66 (KEY_F8) -> xorgevdev:74 */
+ [0x43] = 0x4b, /* qnum:67 -> linux:67 (KEY_F9) -> xorgevdev:75 */
+ [0x44] = 0x4c, /* qnum:68 -> linux:68 (KEY_F10) -> xorgevdev:76 */
+ [0x45] = 0x4d, /* qnum:69 -> linux:69 (KEY_NUMLOCK) -> xorgevdev:77 */
+ [0x46] = 0x4e, /* qnum:70 -> linux:70 (KEY_SCROLLLOCK) -> xorgevdev:78 */
+ [0x47] = 0x4f, /* qnum:71 -> linux:71 (KEY_KP7) -> xorgevdev:79 */
+ [0x48] = 0x50, /* qnum:72 -> linux:72 (KEY_KP8) -> xorgevdev:80 */
+ [0x49] = 0x51, /* qnum:73 -> linux:73 (KEY_KP9) -> xorgevdev:81 */
+ [0x4a] = 0x52, /* qnum:74 -> linux:74 (KEY_KPMINUS) -> xorgevdev:82 */
+ [0x4b] = 0x53, /* qnum:75 -> linux:75 (KEY_KP4) -> xorgevdev:83 */
+ [0x4c] = 0x54, /* qnum:76 -> linux:76 (KEY_KP5) -> xorgevdev:84 */
+ [0x4d] = 0x55, /* qnum:77 -> linux:77 (KEY_KP6) -> xorgevdev:85 */
+ [0x4e] = 0x56, /* qnum:78 -> linux:78 (KEY_KPPLUS) -> xorgevdev:86 */
+ [0x4f] = 0x57, /* qnum:79 -> linux:79 (KEY_KP1) -> xorgevdev:87 */
+ [0x50] = 0x58, /* qnum:80 -> linux:80 (KEY_KP2) -> xorgevdev:88 */
+ [0x51] = 0x59, /* qnum:81 -> linux:81 (KEY_KP3) -> xorgevdev:89 */
+ [0x52] = 0x5a, /* qnum:82 -> linux:82 (KEY_KP0) -> xorgevdev:90 */
+ [0x53] = 0x5b, /* qnum:83 -> linux:83 (KEY_KPDOT) -> xorgevdev:91 */
+ [0x54] = 0x6b, /* qnum:84 -> linux:99 (KEY_SYSRQ) -> xorgevdev:107 */
+ [0x55] = 0xc2, /* qnum:85 -> linux:186 (KEY_F16) -> xorgevdev:194 */
+ [0x56] = 0x5e, /* qnum:86 -> linux:86 (KEY_102ND) -> xorgevdev:94 */
+ [0x57] = 0x5f, /* qnum:87 -> linux:87 (KEY_F11) -> xorgevdev:95 */
+ [0x58] = 0x60, /* qnum:88 -> linux:88 (KEY_F12) -> xorgevdev:96 */
+ [0x59] = 0x7d, /* qnum:89 -> linux:117 (KEY_KPEQUAL) -> xorgevdev:125 */
+ [0x5a] = 0xc6, /* qnum:90 -> linux:190 (KEY_F20) -> xorgevdev:198 */
+ [0x5b] = 0x6d, /* qnum:91 -> linux:101 (KEY_LINEFEED) -> xorgevdev:109 */
+ [0x5c] = 0x67, /* qnum:92 -> linux:95 (KEY_KPJPCOMMA) -> xorgevdev:103 */
+ [0x5d] = 0xbf, /* qnum:93 -> linux:183 (KEY_F13) -> xorgevdev:191 */
+ [0x5e] = 0xc0, /* qnum:94 -> linux:184 (KEY_F14) -> xorgevdev:192 */
+ [0x5f] = 0xc1, /* qnum:95 -> linux:185 (KEY_F15) -> xorgevdev:193 */
+ [0x63] = 0xb1, /* qnum:99 -> linux:169 (KEY_PHONE) -> xorgevdev:177 */
+ [0x64] = 0x8e, /* qnum:100 -> linux:134 (KEY_OPEN) -> xorgevdev:142 */
+ [0x65] = 0x8f, /* qnum:101 -> linux:135 (KEY_PASTE) -> xorgevdev:143 */
+ [0x66] = 0x95, /* qnum:102 -> linux:141 (KEY_SETUP) -> xorgevdev:149 */
+ [0x67] = 0x98, /* qnum:103 -> linux:144 (KEY_FILE) -> xorgevdev:152 */
+ [0x68] = 0x99, /* qnum:104 -> linux:145 (KEY_SENDFILE) -> xorgevdev:153 */
+ [0x69] = 0x9a, /* qnum:105 -> linux:146 (KEY_DELETEFILE) -> xorgevdev:154 */
+ [0x6a] = 0x9f, /* qnum:106 -> linux:151 (KEY_MSDOS) -> xorgevdev:159 */
+ [0x6b] = 0xa1, /* qnum:107 -> linux:153 (KEY_DIRECTION) -> xorgevdev:161 */
+ [0x6c] = 0xa9, /* qnum:108 -> linux:161 (KEY_EJECTCD) -> xorgevdev:169 */
+ [0x6d] = 0xc9, /* qnum:109 -> linux:193 (KEY_F23) -> xorgevdev:201 */
+ [0x6f] = 0xca, /* qnum:111 -> linux:194 (KEY_F24) -> xorgevdev:202 */
+ [0x70] = 0xb2, /* qnum:112 -> linux:170 (KEY_ISO) -> xorgevdev:178 */
+ [0x71] = 0xb6, /* qnum:113 -> linux:174 (KEY_EXIT) -> xorgevdev:182 */
+ [0x72] = 0xb7, /* qnum:114 -> linux:175 (KEY_MOVE) -> xorgevdev:183 */
+ [0x73] = 0x61, /* qnum:115 -> linux:89 (KEY_RO) -> xorgevdev:97 */
+ [0x74] = 0xc7, /* qnum:116 -> linux:191 (KEY_F21) -> xorgevdev:199 */
+ [0x75] = 0xb9, /* qnum:117 -> linux:177 (KEY_SCROLLUP) -> xorgevdev:185 */
+ [0x76] = 0x5d, /* qnum:118 -> linux:85 (KEY_ZENKAKUHANKAKU) -> xorgevdev:93 */
+ [0x77] = 0x63, /* qnum:119 -> linux:91 (KEY_HIRAGANA) -> xorgevdev:99 */
+ [0x78] = 0x62, /* qnum:120 -> linux:90 (KEY_KATAKANA) -> xorgevdev:98 */
+ [0x79] = 0x64, /* qnum:121 -> linux:92 (KEY_HENKAN) -> xorgevdev:100 */
+ [0x7b] = 0x66, /* qnum:123 -> linux:94 (KEY_MUHENKAN) -> xorgevdev:102 */
+ [0x7d] = 0x84, /* qnum:125 -> linux:124 (KEY_YEN) -> xorgevdev:132 */
+ [0x7e] = 0x81, /* qnum:126 -> linux:121 (KEY_KPCOMMA) -> xorgevdev:129 */
+ [0x81] = 0xb3, /* qnum:129 -> linux:171 (KEY_CONFIG) -> xorgevdev:179 */
+ [0x82] = 0x9e, /* qnum:130 -> linux:150 (KEY_WWW) -> xorgevdev:158 */
+ [0x83] = 0xc3, /* qnum:131 -> linux:187 (KEY_F17) -> xorgevdev:195 */
+ [0x84] = 0xc5, /* qnum:132 -> linux:189 (KEY_F19) -> xorgevdev:197 */
+ [0x85] = 0x89, /* qnum:133 -> linux:129 (KEY_AGAIN) -> xorgevdev:137 */
+ [0x86] = 0x8a, /* qnum:134 -> linux:130 (KEY_PROPS) -> xorgevdev:138 */
+ [0x87] = 0x8b, /* qnum:135 -> linux:131 (KEY_UNDO) -> xorgevdev:139 */
+ [0x88] = 0xb8, /* qnum:136 -> linux:176 (KEY_EDIT) -> xorgevdev:184 */
+ [0x89] = 0xbd, /* qnum:137 -> linux:181 (KEY_NEW) -> xorgevdev:189 */
+ [0x8a] = 0xbe, /* qnum:138 -> linux:182 (KEY_REDO) -> xorgevdev:190 */
+ [0x8b] = 0x80, /* qnum:139 -> linux:120 (KEY_SCALE) -> xorgevdev:128 */
+ [0x8c] = 0x8c, /* qnum:140 -> linux:132 (KEY_FRONT) -> xorgevdev:140 */
+ [0x8d] = 0x83, /* qnum:141 -> linux:123 (KEY_HANJA) -> xorgevdev:131 */
+ [0x8e] = 0xf1, /* qnum:142 -> linux:233 (KEY_FORWARDMAIL) -> xorgevdev:241 */
+ [0x8f] = 0xba, /* qnum:143 -> linux:178 (KEY_SCROLLDOWN) -> xorgevdev:186 */
+ [0x90] = 0xad, /* qnum:144 -> linux:165 (KEY_PREVIOUSSONG) -> xorgevdev:173 */
+ [0x92] = 0xa0, /* qnum:146 -> linux:152 (KEY_SCREENLOCK) -> xorgevdev:160 */
+ [0x93] = 0x9b, /* qnum:147 -> linux:147 (KEY_XFER) -> xorgevdev:155 */
+ [0x94] = 0xe6, /* qnum:148 -> linux:222 (KEY_ALTERASE) -> xorgevdev:230 */
+ [0x95] = 0xcb, /* qnum:149 -> linux:195 (unnamed) -> xorgevdev:203 */
+ [0x96] = 0xcc, /* qnum:150 -> linux:196 (unnamed) -> xorgevdev:204 */
+ [0x97] = 0x9d, /* qnum:151 -> linux:149 (KEY_PROG2) -> xorgevdev:157 */
+ [0x98] = 0xb0, /* qnum:152 -> linux:168 (KEY_REWIND) -> xorgevdev:176 */
+ [0x99] = 0xab, /* qnum:153 -> linux:163 (KEY_NEXTSONG) -> xorgevdev:171 */
+ [0x9a] = 0xcd, /* qnum:154 -> linux:197 (unnamed) -> xorgevdev:205 */
+ [0x9b] = 0xce, /* qnum:155 -> linux:198 (unnamed) -> xorgevdev:206 */
+ [0x9c] = 0x68, /* qnum:156 -> linux:96 (KEY_KPENTER) -> xorgevdev:104 */
+ [0x9d] = 0x69, /* qnum:157 -> linux:97 (KEY_RIGHTCTRL) -> xorgevdev:105 */
+ [0x9e] = 0x93, /* qnum:158 -> linux:139 (KEY_MENU) -> xorgevdev:147 */
+ [0x9f] = 0x9c, /* qnum:159 -> linux:148 (KEY_PROG1) -> xorgevdev:156 */
+ [0xa0] = 0x79, /* qnum:160 -> linux:113 (KEY_MUTE) -> xorgevdev:121 */
+ [0xa1] = 0x94, /* qnum:161 -> linux:140 (KEY_CALC) -> xorgevdev:148 */
+ [0xa2] = 0xac, /* qnum:162 -> linux:164 (KEY_PLAYPAUSE) -> xorgevdev:172 */
+ [0xa3] = 0xa8, /* qnum:163 -> linux:160 (KEY_CLOSECD) -> xorgevdev:168 */
+ [0xa4] = 0xae, /* qnum:164 -> linux:166 (KEY_STOPCD) -> xorgevdev:174 */
+ [0xa5] = 0xd5, /* qnum:165 -> linux:205 (KEY_SUSPEND) -> xorgevdev:213 */
+ [0xa6] = 0xa2, /* qnum:166 -> linux:154 (KEY_CYCLEWINDOWS) -> xorgevdev:162 */
+ [0xa7] = 0xcf, /* qnum:167 -> linux:199 (unnamed) -> xorgevdev:207 */
+ [0xa8] = 0xd0, /* qnum:168 -> linux:200 (KEY_PLAYCD) -> xorgevdev:208 */
+ [0xa9] = 0xd1, /* qnum:169 -> linux:201 (KEY_PAUSECD) -> xorgevdev:209 */
+ [0xab] = 0xd2, /* qnum:171 -> linux:202 (KEY_PROG3) -> xorgevdev:210 */
+ [0xac] = 0xd3, /* qnum:172 -> linux:203 (KEY_PROG4) -> xorgevdev:211 */
+ [0xad] = 0xd4, /* qnum:173 -> linux:204 (KEY_DASHBOARD) -> xorgevdev:212 */
+ [0xae] = 0x7a, /* qnum:174 -> linux:114 (KEY_VOLUMEDOWN) -> xorgevdev:122 */
+ [0xaf] = 0xd6, /* qnum:175 -> linux:206 (KEY_CLOSE) -> xorgevdev:214 */
+ [0xb0] = 0x7b, /* qnum:176 -> linux:115 (KEY_VOLUMEUP) -> xorgevdev:123 */
+ [0xb1] = 0xaf, /* qnum:177 -> linux:167 (KEY_RECORD) -> xorgevdev:175 */
+ [0xb2] = 0xb4, /* qnum:178 -> linux:172 (KEY_HOMEPAGE) -> xorgevdev:180 */
+ [0xb3] = 0xd7, /* qnum:179 -> linux:207 (KEY_PLAY) -> xorgevdev:215 */
+ [0xb4] = 0xd8, /* qnum:180 -> linux:208 (KEY_FASTFORWARD) -> xorgevdev:216 */
+ [0xb5] = 0x6a, /* qnum:181 -> linux:98 (KEY_KPSLASH) -> xorgevdev:106 */
+ [0xb6] = 0xd9, /* qnum:182 -> linux:209 (KEY_BASSBOOST) -> xorgevdev:217 */
+ [0xb8] = 0x6c, /* qnum:184 -> linux:100 (KEY_RIGHTALT) -> xorgevdev:108 */
+ [0xb9] = 0xda, /* qnum:185 -> linux:210 (KEY_PRINT) -> xorgevdev:218 */
+ [0xba] = 0xdb, /* qnum:186 -> linux:211 (KEY_HP) -> xorgevdev:219 */
+ [0xbb] = 0xdc, /* qnum:187 -> linux:212 (KEY_CAMERA) -> xorgevdev:220 */
+ [0xbc] = 0x91, /* qnum:188 -> linux:137 (KEY_CUT) -> xorgevdev:145 */
+ [0xbd] = 0xdd, /* qnum:189 -> linux:213 (KEY_SOUND) -> xorgevdev:221 */
+ [0xbe] = 0xde, /* qnum:190 -> linux:214 (KEY_QUESTION) -> xorgevdev:222 */
+ [0xbf] = 0xdf, /* qnum:191 -> linux:215 (KEY_EMAIL) -> xorgevdev:223 */
+ [0xc0] = 0xe0, /* qnum:192 -> linux:216 (KEY_CHAT) -> xorgevdev:224 */
+ [0xc1] = 0x90, /* qnum:193 -> linux:136 (KEY_FIND) -> xorgevdev:144 */
+ [0xc2] = 0xe2, /* qnum:194 -> linux:218 (KEY_CONNECT) -> xorgevdev:226 */
+ [0xc3] = 0xe3, /* qnum:195 -> linux:219 (KEY_FINANCE) -> xorgevdev:227 */
+ [0xc4] = 0xe4, /* qnum:196 -> linux:220 (KEY_SPORT) -> xorgevdev:228 */
+ [0xc5] = 0xe5, /* qnum:197 -> linux:221 (KEY_SHOP) -> xorgevdev:229 */
+ [0xc6] = 0x7f, /* qnum:198 -> linux:119 (KEY_PAUSE) -> xorgevdev:127 */
+ [0xc7] = 0x6e, /* qnum:199 -> linux:102 (KEY_HOME) -> xorgevdev:110 */
+ [0xc8] = 0x6f, /* qnum:200 -> linux:103 (KEY_UP) -> xorgevdev:111 */
+ [0xc9] = 0x70, /* qnum:201 -> linux:104 (KEY_PAGEUP) -> xorgevdev:112 */
+ [0xca] = 0xe7, /* qnum:202 -> linux:223 (KEY_CANCEL) -> xorgevdev:231 */
+ [0xcb] = 0x71, /* qnum:203 -> linux:105 (KEY_LEFT) -> xorgevdev:113 */
+ [0xcc] = 0xe8, /* qnum:204 -> linux:224 (KEY_BRIGHTNESSDOWN) -> xorgevdev:232 */
+ [0xcd] = 0x72, /* qnum:205 -> linux:106 (KEY_RIGHT) -> xorgevdev:114 */
+ [0xce] = 0x7e, /* qnum:206 -> linux:118 (KEY_KPPLUSMINUS) -> xorgevdev:126 */
+ [0xcf] = 0x73, /* qnum:207 -> linux:107 (KEY_END) -> xorgevdev:115 */
+ [0xd0] = 0x74, /* qnum:208 -> linux:108 (KEY_DOWN) -> xorgevdev:116 */
+ [0xd1] = 0x75, /* qnum:209 -> linux:109 (KEY_PAGEDOWN) -> xorgevdev:117 */
+ [0xd2] = 0x76, /* qnum:210 -> linux:110 (KEY_INSERT) -> xorgevdev:118 */
+ [0xd3] = 0x77, /* qnum:211 -> linux:111 (KEY_DELETE) -> xorgevdev:119 */
+ [0xd4] = 0xe9, /* qnum:212 -> linux:225 (KEY_BRIGHTNESSUP) -> xorgevdev:233 */
+ [0xd5] = 0xf2, /* qnum:213 -> linux:234 (KEY_SAVE) -> xorgevdev:242 */
+ [0xd6] = 0xeb, /* qnum:214 -> linux:227 (KEY_SWITCHVIDEOMODE) -> xorgevdev:235 */
+ [0xd7] = 0xec, /* qnum:215 -> linux:228 (KEY_KBDILLUMTOGGLE) -> xorgevdev:236 */
+ [0xd8] = 0xed, /* qnum:216 -> linux:229 (KEY_KBDILLUMDOWN) -> xorgevdev:237 */
+ [0xd9] = 0xee, /* qnum:217 -> linux:230 (KEY_KBDILLUMUP) -> xorgevdev:238 */
+ [0xda] = 0xef, /* qnum:218 -> linux:231 (KEY_SEND) -> xorgevdev:239 */
+ [0xdb] = 0x85, /* qnum:219 -> linux:125 (KEY_LEFTMETA) -> xorgevdev:133 */
+ [0xdc] = 0x86, /* qnum:220 -> linux:126 (KEY_RIGHTMETA) -> xorgevdev:134 */
+ [0xdd] = 0x87, /* qnum:221 -> linux:127 (KEY_COMPOSE) -> xorgevdev:135 */
+ [0xde] = 0x7c, /* qnum:222 -> linux:116 (KEY_POWER) -> xorgevdev:124 */
+ [0xdf] = 0x96, /* qnum:223 -> linux:142 (KEY_SLEEP) -> xorgevdev:150 */
+ [0xe3] = 0x97, /* qnum:227 -> linux:143 (KEY_WAKEUP) -> xorgevdev:151 */
+ [0xe4] = 0xf0, /* qnum:228 -> linux:232 (KEY_REPLY) -> xorgevdev:240 */
+ [0xe5] = 0xe1, /* qnum:229 -> linux:217 (KEY_SEARCH) -> xorgevdev:225 */
+ [0xe6] = 0xa4, /* qnum:230 -> linux:156 (KEY_BOOKMARKS) -> xorgevdev:164 */
+ [0xe7] = 0xb5, /* qnum:231 -> linux:173 (KEY_REFRESH) -> xorgevdev:181 */
+ [0xe8] = 0x88, /* qnum:232 -> linux:128 (KEY_STOP) -> xorgevdev:136 */
+ [0xe9] = 0xa7, /* qnum:233 -> linux:159 (KEY_FORWARD) -> xorgevdev:167 */
+ [0xea] = 0xa6, /* qnum:234 -> linux:158 (KEY_BACK) -> xorgevdev:166 */
+ [0xeb] = 0xa5, /* qnum:235 -> linux:157 (KEY_COMPUTER) -> xorgevdev:165 */
+ [0xec] = 0xa3, /* qnum:236 -> linux:155 (KEY_MAIL) -> xorgevdev:163 */
+ [0xed] = 0xea, /* qnum:237 -> linux:226 (KEY_MEDIA) -> xorgevdev:234 */
+ [0xef] = 0x78, /* qnum:239 -> linux:112 (KEY_MACRO) -> xorgevdev:120 */
+ [0xf0] = 0xf3, /* qnum:240 -> linux:235 (KEY_DOCUMENTS) -> xorgevdev:243 */
+ [0xf1] = 0xf4, /* qnum:241 -> linux:236 (KEY_BATTERY) -> xorgevdev:244 */
+ [0xf2] = 0xf5, /* qnum:242 -> linux:237 (KEY_BLUETOOTH) -> xorgevdev:245 */
+ [0xf3] = 0xf6, /* qnum:243 -> linux:238 (KEY_WLAN) -> xorgevdev:246 */
+ [0xf4] = 0xf7, /* qnum:244 -> linux:239 (KEY_UWB) -> xorgevdev:247 */
+ [0xf5] = 0x92, /* qnum:245 -> linux:138 (KEY_HELP) -> xorgevdev:146 */
+ [0xf6] = 0xbb, /* qnum:246 -> linux:179 (KEY_KPLEFTPAREN) -> xorgevdev:187 */
+ [0xf7] = 0xc4, /* qnum:247 -> linux:188 (KEY_F18) -> xorgevdev:196 */
+ [0xf8] = 0x8d, /* qnum:248 -> linux:133 (KEY_COPY) -> xorgevdev:141 */
+ [0xf9] = 0xc8, /* qnum:249 -> linux:192 (KEY_F22) -> xorgevdev:200 */
+ [0xfb] = 0xbc, /* qnum:251 -> linux:180 (KEY_KPRIGHTPAREN) -> xorgevdev:188 */
+ [0xfd] = 0xaa, /* qnum:253 -> linux:162 (KEY_EJECTCLOSECD) -> xorgevdev:170 */
+};
+const unsigned int code_map_qnum_to_xorgevdev_len = sizeof(code_map_qnum_to_xorgevdev)/sizeof(code_map_qnum_to_xorgevdev[0]);
diff --git a/unix/xserver/hw/vnc/qnum_to_xorgkbd.c b/unix/xserver/hw/vnc/qnum_to_xorgkbd.c
new file mode 100644
index 0000000..57c2047
--- /dev/null
+++ b/unix/xserver/hw/vnc/qnum_to_xorgkbd.c
@@ -0,0 +1,121 @@
+/*
+ * This file is auto-generated from keymaps.csv on 2017-08-28 13:04
+ * Database checksum sha256(f8aeff0c3430077a350e3d7ba2b335b381bd929ac4b193413730a402ff3f0097)
+ * To re-generate, run:
+ * keymap-gen --lang=stdc code-map keymaps.csv qnum xorgkbd
+*/
+const unsigned short code_map_qnum_to_xorgkbd[254] = {
+ [0x1] = 0x9, /* qnum:1 -> linux:1 (KEY_ESC) -> xorgkbd:9 */
+ [0x2] = 0xa, /* qnum:2 -> linux:2 (KEY_1) -> xorgkbd:10 */
+ [0x3] = 0xb, /* qnum:3 -> linux:3 (KEY_2) -> xorgkbd:11 */
+ [0x4] = 0xc, /* qnum:4 -> linux:4 (KEY_3) -> xorgkbd:12 */
+ [0x5] = 0xd, /* qnum:5 -> linux:5 (KEY_4) -> xorgkbd:13 */
+ [0x6] = 0xe, /* qnum:6 -> linux:6 (KEY_5) -> xorgkbd:14 */
+ [0x7] = 0xf, /* qnum:7 -> linux:7 (KEY_6) -> xorgkbd:15 */
+ [0x8] = 0x10, /* qnum:8 -> linux:8 (KEY_7) -> xorgkbd:16 */
+ [0x9] = 0x11, /* qnum:9 -> linux:9 (KEY_8) -> xorgkbd:17 */
+ [0xa] = 0x12, /* qnum:10 -> linux:10 (KEY_9) -> xorgkbd:18 */
+ [0xb] = 0x13, /* qnum:11 -> linux:11 (KEY_0) -> xorgkbd:19 */
+ [0xc] = 0x14, /* qnum:12 -> linux:12 (KEY_MINUS) -> xorgkbd:20 */
+ [0xd] = 0x15, /* qnum:13 -> linux:13 (KEY_EQUAL) -> xorgkbd:21 */
+ [0xe] = 0x16, /* qnum:14 -> linux:14 (KEY_BACKSPACE) -> xorgkbd:22 */
+ [0xf] = 0x17, /* qnum:15 -> linux:15 (KEY_TAB) -> xorgkbd:23 */
+ [0x10] = 0x18, /* qnum:16 -> linux:16 (KEY_Q) -> xorgkbd:24 */
+ [0x11] = 0x19, /* qnum:17 -> linux:17 (KEY_W) -> xorgkbd:25 */
+ [0x12] = 0x1a, /* qnum:18 -> linux:18 (KEY_E) -> xorgkbd:26 */
+ [0x13] = 0x1b, /* qnum:19 -> linux:19 (KEY_R) -> xorgkbd:27 */
+ [0x14] = 0x1c, /* qnum:20 -> linux:20 (KEY_T) -> xorgkbd:28 */
+ [0x15] = 0x1d, /* qnum:21 -> linux:21 (KEY_Y) -> xorgkbd:29 */
+ [0x16] = 0x1e, /* qnum:22 -> linux:22 (KEY_U) -> xorgkbd:30 */
+ [0x17] = 0x1f, /* qnum:23 -> linux:23 (KEY_I) -> xorgkbd:31 */
+ [0x18] = 0x20, /* qnum:24 -> linux:24 (KEY_O) -> xorgkbd:32 */
+ [0x19] = 0x21, /* qnum:25 -> linux:25 (KEY_P) -> xorgkbd:33 */
+ [0x1a] = 0x22, /* qnum:26 -> linux:26 (KEY_LEFTBRACE) -> xorgkbd:34 */
+ [0x1b] = 0x23, /* qnum:27 -> linux:27 (KEY_RIGHTBRACE) -> xorgkbd:35 */
+ [0x1c] = 0x24, /* qnum:28 -> linux:28 (KEY_ENTER) -> xorgkbd:36 */
+ [0x1d] = 0x25, /* qnum:29 -> linux:29 (KEY_LEFTCTRL) -> xorgkbd:37 */
+ [0x1e] = 0x26, /* qnum:30 -> linux:30 (KEY_A) -> xorgkbd:38 */
+ [0x1f] = 0x27, /* qnum:31 -> linux:31 (KEY_S) -> xorgkbd:39 */
+ [0x20] = 0x28, /* qnum:32 -> linux:32 (KEY_D) -> xorgkbd:40 */
+ [0x21] = 0x29, /* qnum:33 -> linux:33 (KEY_F) -> xorgkbd:41 */
+ [0x22] = 0x2a, /* qnum:34 -> linux:34 (KEY_G) -> xorgkbd:42 */
+ [0x23] = 0x2b, /* qnum:35 -> linux:35 (KEY_H) -> xorgkbd:43 */
+ [0x24] = 0x2c, /* qnum:36 -> linux:36 (KEY_J) -> xorgkbd:44 */
+ [0x25] = 0x2d, /* qnum:37 -> linux:37 (KEY_K) -> xorgkbd:45 */
+ [0x26] = 0x2e, /* qnum:38 -> linux:38 (KEY_L) -> xorgkbd:46 */
+ [0x27] = 0x2f, /* qnum:39 -> linux:39 (KEY_SEMICOLON) -> xorgkbd:47 */
+ [0x28] = 0x30, /* qnum:40 -> linux:40 (KEY_APOSTROPHE) -> xorgkbd:48 */
+ [0x29] = 0x31, /* qnum:41 -> linux:41 (KEY_GRAVE) -> xorgkbd:49 */
+ [0x2a] = 0x32, /* qnum:42 -> linux:42 (KEY_LEFTSHIFT) -> xorgkbd:50 */
+ [0x2b] = 0x33, /* qnum:43 -> linux:43 (KEY_BACKSLASH) -> xorgkbd:51 */
+ [0x2c] = 0x34, /* qnum:44 -> linux:44 (KEY_Z) -> xorgkbd:52 */
+ [0x2d] = 0x35, /* qnum:45 -> linux:45 (KEY_X) -> xorgkbd:53 */
+ [0x2e] = 0x36, /* qnum:46 -> linux:46 (KEY_C) -> xorgkbd:54 */
+ [0x2f] = 0x37, /* qnum:47 -> linux:47 (KEY_V) -> xorgkbd:55 */
+ [0x30] = 0x38, /* qnum:48 -> linux:48 (KEY_B) -> xorgkbd:56 */
+ [0x31] = 0x39, /* qnum:49 -> linux:49 (KEY_N) -> xorgkbd:57 */
+ [0x32] = 0x3a, /* qnum:50 -> linux:50 (KEY_M) -> xorgkbd:58 */
+ [0x33] = 0x3b, /* qnum:51 -> linux:51 (KEY_COMMA) -> xorgkbd:59 */
+ [0x34] = 0x3c, /* qnum:52 -> linux:52 (KEY_DOT) -> xorgkbd:60 */
+ [0x35] = 0x3d, /* qnum:53 -> linux:53 (KEY_SLASH) -> xorgkbd:61 */
+ [0x36] = 0x3e, /* qnum:54 -> linux:54 (KEY_RIGHTSHIFT) -> xorgkbd:62 */
+ [0x37] = 0x3f, /* qnum:55 -> linux:55 (KEY_KPASTERISK) -> xorgkbd:63 */
+ [0x38] = 0x40, /* qnum:56 -> linux:56 (KEY_LEFTALT) -> xorgkbd:64 */
+ [0x39] = 0x41, /* qnum:57 -> linux:57 (KEY_SPACE) -> xorgkbd:65 */
+ [0x3a] = 0x42, /* qnum:58 -> linux:58 (KEY_CAPSLOCK) -> xorgkbd:66 */
+ [0x3b] = 0x43, /* qnum:59 -> linux:59 (KEY_F1) -> xorgkbd:67 */
+ [0x3c] = 0x44, /* qnum:60 -> linux:60 (KEY_F2) -> xorgkbd:68 */
+ [0x3d] = 0x45, /* qnum:61 -> linux:61 (KEY_F3) -> xorgkbd:69 */
+ [0x3e] = 0x46, /* qnum:62 -> linux:62 (KEY_F4) -> xorgkbd:70 */
+ [0x3f] = 0x47, /* qnum:63 -> linux:63 (KEY_F5) -> xorgkbd:71 */
+ [0x40] = 0x48, /* qnum:64 -> linux:64 (KEY_F6) -> xorgkbd:72 */
+ [0x41] = 0x49, /* qnum:65 -> linux:65 (KEY_F7) -> xorgkbd:73 */
+ [0x42] = 0x4a, /* qnum:66 -> linux:66 (KEY_F8) -> xorgkbd:74 */
+ [0x43] = 0x4b, /* qnum:67 -> linux:67 (KEY_F9) -> xorgkbd:75 */
+ [0x44] = 0x4c, /* qnum:68 -> linux:68 (KEY_F10) -> xorgkbd:76 */
+ [0x45] = 0x4d, /* qnum:69 -> linux:69 (KEY_NUMLOCK) -> xorgkbd:77 */
+ [0x46] = 0x4e, /* qnum:70 -> linux:70 (KEY_SCROLLLOCK) -> xorgkbd:78 */
+ [0x47] = 0x4f, /* qnum:71 -> linux:71 (KEY_KP7) -> xorgkbd:79 */
+ [0x48] = 0x50, /* qnum:72 -> linux:72 (KEY_KP8) -> xorgkbd:80 */
+ [0x49] = 0x51, /* qnum:73 -> linux:73 (KEY_KP9) -> xorgkbd:81 */
+ [0x4a] = 0x52, /* qnum:74 -> linux:74 (KEY_KPMINUS) -> xorgkbd:82 */
+ [0x4b] = 0x53, /* qnum:75 -> linux:75 (KEY_KP4) -> xorgkbd:83 */
+ [0x4c] = 0x54, /* qnum:76 -> linux:76 (KEY_KP5) -> xorgkbd:84 */
+ [0x4d] = 0x55, /* qnum:77 -> linux:77 (KEY_KP6) -> xorgkbd:85 */
+ [0x4e] = 0x56, /* qnum:78 -> linux:78 (KEY_KPPLUS) -> xorgkbd:86 */
+ [0x4f] = 0x57, /* qnum:79 -> linux:79 (KEY_KP1) -> xorgkbd:87 */
+ [0x50] = 0x58, /* qnum:80 -> linux:80 (KEY_KP2) -> xorgkbd:88 */
+ [0x51] = 0x59, /* qnum:81 -> linux:81 (KEY_KP3) -> xorgkbd:89 */
+ [0x52] = 0x5a, /* qnum:82 -> linux:82 (KEY_KP0) -> xorgkbd:90 */
+ [0x53] = 0x5b, /* qnum:83 -> linux:83 (KEY_KPDOT) -> xorgkbd:91 */
+ [0x54] = 0x6f, /* qnum:84 -> linux:99 (KEY_SYSRQ) -> xorgkbd:111 */
+ [0x55] = 0x79, /* qnum:85 -> linux:186 (KEY_F16) -> xorgkbd:121 */
+ [0x56] = 0x5e, /* qnum:86 -> linux:86 (KEY_102ND) -> xorgkbd:94 */
+ [0x57] = 0x5f, /* qnum:87 -> linux:87 (KEY_F11) -> xorgkbd:95 */
+ [0x58] = 0x60, /* qnum:88 -> linux:88 (KEY_F12) -> xorgkbd:96 */
+ [0x59] = 0x7e, /* qnum:89 -> linux:117 (KEY_KPEQUAL) -> xorgkbd:126 */
+ [0x5d] = 0x76, /* qnum:93 -> linux:183 (KEY_F13) -> xorgkbd:118 */
+ [0x5e] = 0x77, /* qnum:94 -> linux:184 (KEY_F14) -> xorgkbd:119 */
+ [0x5f] = 0x78, /* qnum:95 -> linux:185 (KEY_F15) -> xorgkbd:120 */
+ [0x7d] = 0x85, /* qnum:125 -> linux:124 (KEY_YEN) -> xorgkbd:133 */
+ [0x83] = 0x7a, /* qnum:131 -> linux:187 (KEY_F17) -> xorgkbd:122 */
+ [0x9c] = 0x6c, /* qnum:156 -> linux:96 (KEY_KPENTER) -> xorgkbd:108 */
+ [0x9d] = 0x6d, /* qnum:157 -> linux:97 (KEY_RIGHTCTRL) -> xorgkbd:109 */
+ [0xb5] = 0x70, /* qnum:181 -> linux:98 (KEY_KPSLASH) -> xorgkbd:112 */
+ [0xb8] = 0x71, /* qnum:184 -> linux:100 (KEY_RIGHTALT) -> xorgkbd:113 */
+ [0xc6] = 0x6e, /* qnum:198 -> linux:119 (KEY_PAUSE) -> xorgkbd:110 */
+ [0xc7] = 0x61, /* qnum:199 -> linux:102 (KEY_HOME) -> xorgkbd:97 */
+ [0xc8] = 0x62, /* qnum:200 -> linux:103 (KEY_UP) -> xorgkbd:98 */
+ [0xc9] = 0x63, /* qnum:201 -> linux:104 (KEY_PAGEUP) -> xorgkbd:99 */
+ [0xcb] = 0x64, /* qnum:203 -> linux:105 (KEY_LEFT) -> xorgkbd:100 */
+ [0xcd] = 0x66, /* qnum:205 -> linux:106 (KEY_RIGHT) -> xorgkbd:102 */
+ [0xcf] = 0x67, /* qnum:207 -> linux:107 (KEY_END) -> xorgkbd:103 */
+ [0xd0] = 0x68, /* qnum:208 -> linux:108 (KEY_DOWN) -> xorgkbd:104 */
+ [0xd1] = 0x69, /* qnum:209 -> linux:109 (KEY_PAGEDOWN) -> xorgkbd:105 */
+ [0xd2] = 0x6a, /* qnum:210 -> linux:110 (KEY_INSERT) -> xorgkbd:106 */
+ [0xd3] = 0x6b, /* qnum:211 -> linux:111 (KEY_DELETE) -> xorgkbd:107 */
+ [0xdb] = 0x73, /* qnum:219 -> linux:125 (KEY_LEFTMETA) -> xorgkbd:115 */
+ [0xdc] = 0x74, /* qnum:220 -> linux:126 (KEY_RIGHTMETA) -> xorgkbd:116 */
+ [0xdd] = 0x75, /* qnum:221 -> linux:127 (KEY_COMPOSE) -> xorgkbd:117 */
+};
+const unsigned int code_map_qnum_to_xorgkbd_len = sizeof(code_map_qnum_to_xorgkbd)/sizeof(code_map_qnum_to_xorgkbd[0]);
diff --git a/unix/xserver/hw/vnc/vncExtInit.cc b/unix/xserver/hw/vnc/vncExtInit.cc
index 57bf6d8..13248f9 100644
--- a/unix/xserver/hw/vnc/vncExtInit.cc
+++ b/unix/xserver/hw/vnc/vncExtInit.cc
@@ -32,6 +32,7 @@
#include <rfb/LogWriter.h>
#include <rfb/Hostname.h>
#include <rfb/Region.h>
+#include <rfb/ledStates.h>
#include <network/TcpSocket.h>
#include "XserverDesktop.h"
@@ -358,6 +359,25 @@
}
}
+void vncSetLEDState(unsigned long leds)
+{
+ unsigned int state;
+
+ state = 0;
+ if (leds & (1 << 0))
+ state |= ledCapsLock;
+ if (leds & (1 << 1))
+ state |= ledNumLock;
+ if (leds & (1 << 2))
+ state |= ledScrollLock;
+
+ for (int scr = 0; scr < vncGetScreenCount(); scr++) {
+ if (desktop[scr] == NULL)
+ continue;
+ desktop[scr]->setLEDState(state);
+ }
+}
+
void vncAddChanged(int scrIdx, const struct UpdateRect *extents,
int nRects, const struct UpdateRect *rects)
{
diff --git a/unix/xserver/hw/vnc/vncExtInit.h b/unix/xserver/hw/vnc/vncExtInit.h
index 9f8d9e7..99fee27 100644
--- a/unix/xserver/hw/vnc/vncExtInit.h
+++ b/unix/xserver/hw/vnc/vncExtInit.h
@@ -70,6 +70,8 @@
void vncBell(void);
+void vncSetLEDState(unsigned long leds);
+
// Must match rfb::ShortRect in common/rfb/Region.h, and BoxRec in the
// Xorg source.
struct UpdateRect {
diff --git a/vncviewer/CConn.cxx b/vncviewer/CConn.cxx
index addc30d..07e7841 100644
--- a/vncviewer/CConn.cxx
+++ b/vncviewer/CConn.cxx
@@ -92,6 +92,8 @@
cp.supportsExtendedDesktopSize = true;
cp.supportsDesktopRename = true;
+ cp.supportsLEDState = true;
+
if (customCompressLevel)
cp.compressLevel = compressLevel;
else
@@ -110,7 +112,8 @@
vlog.info(_("connected to host %s port %d"), serverHost, serverPort);
} catch (rdr::Exception& e) {
vlog.error("%s", e.str());
- fl_alert("%s", e.str());
+ if (alertOnFatalError)
+ fl_alert("%s", e.str());
exit_vncviewer();
return;
}
@@ -502,6 +505,13 @@
}
}
+void CConn::setLEDState(unsigned int state)
+{
+ CConnection::setLEDState(state);
+
+ desktop->setLEDState(state);
+}
+
////////////////////// Internal methods //////////////////////
diff --git a/vncviewer/CConn.h b/vncviewer/CConn.h
index 93cc278..426bd1e 100644
--- a/vncviewer/CConn.h
+++ b/vncviewer/CConn.h
@@ -74,6 +74,8 @@
void fence(rdr::U32 flags, unsigned len, const char data[]);
+ void setLEDState(unsigned int state);
+
private:
void resizeFramebuffer();
diff --git a/vncviewer/CMakeLists.txt b/vncviewer/CMakeLists.txt
index 1833e70..3c18646 100644
--- a/vncviewer/CMakeLists.txt
+++ b/vncviewer/CMakeLists.txt
@@ -29,7 +29,9 @@
if(WIN32)
set(VNCVIEWER_SOURCES ${VNCVIEWER_SOURCES} win32.c)
elseif(APPLE)
- set(VNCVIEWER_SOURCES ${VNCVIEWER_SOURCES} cocoa.mm)
+ set(VNCVIEWER_SOURCES ${VNCVIEWER_SOURCES} cocoa.mm osx_to_qnum.c)
+else()
+ set(VNCVIEWER_SOURCES ${VNCVIEWER_SOURCES} xkb_to_qnum.c)
endif()
if(WIN32)
@@ -53,14 +55,44 @@
endif()
if(APPLE)
- target_link_libraries(vncviewer "-framework Cocoa" "-framework Carbon")
+ target_link_libraries(vncviewer "-framework Cocoa")
+ target_link_libraries(vncviewer "-framework Carbon")
+ target_link_libraries(vncviewer "-framework IOKit")
endif()
install(TARGETS vncviewer DESTINATION ${BIN_DIR})
if(UNIX)
install(FILES vncviewer.man DESTINATION ${MAN_DIR}/man1 RENAME vncviewer.1)
- configure_file(vncviewer.desktop.in vncviewer.desktop)
+
+ configure_file(vncviewer.desktop.in.in vncviewer.desktop.in)
+ find_program(INTLTOOL_MERGE_EXECUTABLE intltool-merge)
+ if("${GETTEXT_VERSION_STRING}" VERSION_GREATER 0.18.99)
+ add_custom_command(OUTPUT vncviewer.desktop
+ COMMAND ${GETTEXT_MSGFMT_EXECUTABLE}
+ --desktop --template vncviewer.desktop.in
+ -d ${CMAKE_SOURCE_DIR}/po -o vncviewer.desktop
+ DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/vncviewer.desktop.in
+ )
+ elseif(INTLTOOL_MERGE_EXECUTABLE)
+ add_custom_command(OUTPUT vncviewer.desktop
+ COMMAND sed -e 's/^Name/_Name/'
+ -e 's/^GenericName/_GenericName/'
+ -e 's/^Comment/_Comment/'
+ vncviewer.desktop.in > vncviewer.desktop.intl
+ COMMAND ${INTLTOOL_MERGE_EXECUTABLE}
+ -d ${CMAKE_SOURCE_DIR}/po
+ vncviewer.desktop.intl vncviewer.desktop
+ DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/vncviewer.desktop.in
+ )
+ else()
+ add_custom_command(OUTPUT vncviewer.desktop
+ COMMAND cp vncviewer.desktop.in vncviewer.desktop
+ DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/vncviewer.desktop.in
+ )
+ endif()
+ add_custom_target(desktop ALL DEPENDS vncviewer.desktop)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/vncviewer.desktop DESTINATION ${DATA_DIR}/applications)
+
foreach(res 16 22 24 32 48)
install(FILES ../media/icons/tigervnc_${res}.png DESTINATION ${DATA_DIR}/icons/hicolor/${res}x${res}/apps RENAME tigervnc.png)
endforeach()
diff --git a/vncviewer/DesktopWindow.cxx b/vncviewer/DesktopWindow.cxx
index 408efd1..897f955 100644
--- a/vncviewer/DesktopWindow.cxx
+++ b/vncviewer/DesktopWindow.cxx
@@ -395,6 +395,12 @@
}
+void DesktopWindow::setLEDState(unsigned int state)
+{
+ viewport->setLEDState(state);
+}
+
+
void DesktopWindow::resize(int x, int y, int w, int h)
{
bool resizing;
@@ -660,23 +666,31 @@
DesktopWindow *dw = dynamic_cast<DesktopWindow*>(win);
- if (dw && fullscreenSystemKeys) {
+ if (dw) {
switch (event) {
case FL_FOCUS:
- // FIXME: We reassert the keyboard grabbing on focus as FLTK there are
- // some issues we need to work around:
- // a) Fl::grab(0) on X11 will release the keyboard grab for us.
- // b) Gaining focus on the system level causes FLTK to switch
- // window level on OS X.
- if (dw->fullscreen_active())
- dw->grabKeyboard();
+ if (fullscreenSystemKeys) {
+ // FIXME: We reassert the keyboard grabbing on focus as FLTK there are
+ // some issues we need to work around:
+ // a) Fl::grab(0) on X11 will release the keyboard grab for us.
+ // b) Gaining focus on the system level causes FLTK to switch
+ // window level on OS X.
+ if (dw->fullscreen_active())
+ dw->grabKeyboard();
+ }
+
+ // We may have gotten our lock keys out of sync with the server
+ // whilst we didn't have focus. Try to sort this out.
+ dw->viewport->pushLEDState();
break;
case FL_UNFOCUS:
- // FIXME: We need to relinquish control when the entire window loses
- // focus as it is very tied to this specific window on some
- // platforms and we want to be able to open subwindows.
- dw->ungrabKeyboard();
+ if (fullscreenSystemKeys) {
+ // FIXME: We need to relinquish control when the entire window loses
+ // focus as it is very tied to this specific window on some
+ // platforms and we want to be able to open subwindows.
+ dw->ungrabKeyboard();
+ }
break;
}
}
diff --git a/vncviewer/DesktopWindow.h b/vncviewer/DesktopWindow.h
index 4224699..f1bf312 100644
--- a/vncviewer/DesktopWindow.h
+++ b/vncviewer/DesktopWindow.h
@@ -66,6 +66,9 @@
void setCursor(int width, int height, const rfb::Point& hotspot,
const rdr::U8* data);
+ // Change client LED state
+ void setLEDState(unsigned int state);
+
// Fl_Window callback methods
void draw();
void resize(int x, int y, int w, int h);
diff --git a/vncviewer/FLTKPixelBuffer.cxx b/vncviewer/FLTKPixelBuffer.cxx
deleted file mode 100644
index ab116d1..0000000
--- a/vncviewer/FLTKPixelBuffer.cxx
+++ /dev/null
@@ -1,53 +0,0 @@
-/* Copyright 2011-2014 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.
- */
-
-#include <FL/fl_draw.H>
-
-#include <rfb/Exception.h>
-
-#include "i18n.h"
-#include "FLTKPixelBuffer.h"
-
-FLTKPixelBuffer::FLTKPixelBuffer(int width, int height) :
- PlatformPixelBuffer(rfb::PixelFormat(32, 24, false, true,
- 255, 255, 255, 0, 8, 16),
- width, height, NULL, width)
-{
- data = new rdr::U8[width * height * format.bpp/8];
- if (data == NULL)
- throw rfb::Exception(_("Not enough memory for framebuffer"));
-}
-
-FLTKPixelBuffer::~FLTKPixelBuffer()
-{
- delete [] data;
-}
-
-void FLTKPixelBuffer::draw(int src_x, int src_y, int x, int y, int w, int h)
-{
- int pixel_bytes, stride_bytes;
- const uchar *buf_start;
-
- pixel_bytes = format.bpp/8;
- stride_bytes = pixel_bytes * stride;
- buf_start = data +
- pixel_bytes * src_x +
- stride_bytes * src_y;
-
- fl_draw_image(buf_start, x, y, w, h, pixel_bytes, stride_bytes);
-}
diff --git a/vncviewer/UserDialog.cxx b/vncviewer/UserDialog.cxx
index b57588d..640f2a9 100644
--- a/vncviewer/UserDialog.cxx
+++ b/vncviewer/UserDialog.cxx
@@ -32,6 +32,7 @@
#include <FL/Fl_Secret_Input.H>
#include <FL/Fl_Button.H>
#include <FL/Fl_Return_Button.H>
+#include <FL/Fl_Pixmap.H>
#include <rfb/util.h>
#include <rfb/Password.h>
@@ -42,8 +43,18 @@
#include "parameters.h"
#include "UserDialog.h"
+/* xpm:s predate const, so they have invalid definitions */
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wwrite-strings"
+#include "../media/secure.xpm"
+#include "../media/insecure.xpm"
+#pragma GCC diagnostic pop
+
using namespace rfb;
+static Fl_Pixmap secure_icon(secure);
+static Fl_Pixmap insecure_icon(insecure);
+
static int ret_val = 0;
static void button_cb(Fl_Widget *widget, void *val) {
@@ -59,7 +70,7 @@
{
}
-void UserDialog::getUserPasswd(char** user, char** password)
+void UserDialog::getUserPasswd(bool secure, char** user, char** password)
{
CharArray passwordFileStr(passwordFile.getData());
@@ -82,46 +93,73 @@
return;
}
- if (!user) {
- fl_message_title(_("VNC authentication"));
- *password = strDup(fl_password(_("Password:"), ""));
- if (!*password)
- throw rfb::Exception(_("Authentication cancelled"));
+ Fl_Window *win;
+ Fl_Box *banner;
+ Fl_Input *username;
+ Fl_Secret_Input *passwd;
+ Fl_Box *icon;
+ Fl_Button *button;
- return;
- }
+ int y;
- // Largely copied from FLTK so that we get the same look and feel
- // as the simpler password input.
- Fl_Window *win = new Fl_Window(410, 145, _("VNC authentication"));
+ win = new Fl_Window(410, 145, _("VNC authentication"));
win->callback(button_cb,(void *)0);
- Fl_Input *username = new Fl_Input(70, 25, 300, 25, _("Username:"));
- username->align(FL_ALIGN_TOP_LEFT);
+ banner = new Fl_Box(0, 0, win->w(), 20);
+ banner->align(FL_ALIGN_CENTER|FL_ALIGN_INSIDE|FL_ALIGN_IMAGE_NEXT_TO_TEXT);
+ banner->box(FL_FLAT_BOX);
+ if (secure) {
+ banner->label(_("This connection is secure"));
+ banner->color(FL_GREEN);
+ banner->image(secure_icon);
+ } else {
+ banner->label(_("This connection is not secure"));
+ banner->color(FL_RED);
+ banner->image(insecure_icon);
+ }
- Fl_Secret_Input *passwd = new Fl_Secret_Input(70, 70, 300, 25, _("Password:"));
- passwd->align(FL_ALIGN_TOP_LEFT);
+ y = 20 + 10;
- Fl_Box *icon = new Fl_Box(10, 10, 50, 50, "?");
+ icon = new Fl_Box(10, y, 50, 50, "?");
icon->box(FL_UP_BOX);
icon->labelfont(FL_TIMES_BOLD);
icon->labelsize(34);
icon->color(FL_WHITE);
icon->labelcolor(FL_BLUE);
- Fl_Button *button;
+ y += 5;
- button = new Fl_Return_Button(310, 110, 90, 25, fl_ok);
+ if (user) {
+ (new Fl_Box(70, y, win->w()-70-10, 20, _("Username:")))
+ ->align(FL_ALIGN_LEFT|FL_ALIGN_INSIDE);
+ y += 20 + 5;
+ username = new Fl_Input(70, y, win->w()-70-10, 25);
+ y += 25 + 5;
+ }
+
+ (new Fl_Box(70, y, win->w()-70-10, 20, _("Password:")))
+ ->align(FL_ALIGN_LEFT|FL_ALIGN_INSIDE);
+ y += 20 + 5;
+ passwd = new Fl_Secret_Input(70, y, win->w()-70-10, 25);
+ y += 25 + 5;
+
+ y += 5;
+
+ button = new Fl_Return_Button(310, y, 90, 25, fl_ok);
button->align(FL_ALIGN_INSIDE|FL_ALIGN_WRAP);
button->callback(button_cb, (void*)0);
- button = new Fl_Button(210, 110, 90, 25, fl_cancel);
+ button = new Fl_Button(210, y, 90, 25, fl_cancel);
button->align(FL_ALIGN_INSIDE|FL_ALIGN_WRAP);
button->callback(button_cb, (void*)1);
button->shortcut(FL_Escape);
+ y += 25 + 10;
+
win->end();
+ win->size(win->w(), y);
+
win->set_modal();
ret_val = -1;
@@ -130,14 +168,15 @@
while (win->shown()) Fl::wait();
if (ret_val == 0) {
- *user = strDup(username->value());
+ if (user)
+ *user = strDup(username->value());
*password = strDup(passwd->value());
- } else {
- *user = strDup("");
- *password = strDup("");
}
delete win;
+
+ if (ret_val != 0)
+ throw rfb::Exception(_("Authentication cancelled"));
}
bool UserDialog::showMsgBox(int flags, const char* title, const char* text)
diff --git a/vncviewer/UserDialog.h b/vncviewer/UserDialog.h
index c6756a8..b62ba7f 100644
--- a/vncviewer/UserDialog.h
+++ b/vncviewer/UserDialog.h
@@ -31,7 +31,7 @@
// UserPasswdGetter callbacks
- void getUserPasswd(char** user, char** password);
+ void getUserPasswd(bool secure, char** user, char** password);
// UserMsgBox callbacks
diff --git a/vncviewer/Viewport.cxx b/vncviewer/Viewport.cxx
index 6a23526..331efbc 100644
--- a/vncviewer/Viewport.cxx
+++ b/vncviewer/Viewport.cxx
@@ -28,6 +28,7 @@
#include <rfb/CMsgWriter.h>
#include <rfb/LogWriter.h>
#include <rfb/Exception.h>
+#include <rfb/ledStates.h>
// FLTK can pull in the X11 headers on some systems
#ifndef XK_VoidSymbol
@@ -41,6 +42,10 @@
#include <rfb/XF86keysym.h>
#endif
+#if ! (defined(WIN32) || defined(__APPLE__))
+#include <X11/XKBlib.h>
+#endif
+
#ifndef NoSymbol
#define NoSymbol 0
#endif
@@ -69,8 +74,21 @@
#include <FL/Fl_Menu.H>
#include <FL/Fl_Menu_Button.H>
+#if !defined(WIN32) && !defined(__APPLE__)
+#include <X11/XKBlib.h>
+extern const struct _code_map_xkb_to_qnum {
+ const char * from;
+ const unsigned short to;
+} code_map_xkb_to_qnum[];
+extern const unsigned int code_map_xkb_to_qnum_len;
+
+static int code_map_keycode_to_qnum[256];
+#endif
+
#ifdef __APPLE__
#include "cocoa.h"
+extern const unsigned short code_map_osx_to_qnum[];
+extern const unsigned int code_map_osx_to_qnum_len;
#endif
#ifdef WIN32
@@ -89,14 +107,55 @@
ID_CTRL, ID_ALT, ID_MENUKEY, ID_CTRLALTDEL,
ID_REFRESH, ID_OPTIONS, ID_INFO, ID_ABOUT, ID_DISMISS };
-// Fake key presses use this value and above
-static const int fakeKeyBase = 0x200;
+// Used to detect fake input (0xaa is not a real key)
+#ifdef WIN32
+static const WORD SCAN_FAKE = 0xaa;
+#endif
Viewport::Viewport(int w, int h, const rfb::PixelFormat& serverPF, CConn* cc_)
: Fl_Widget(0, 0, w, h), cc(cc_), frameBuffer(NULL),
lastPointerPos(0, 0), lastButtonMask(0),
menuCtrlKey(false), menuAltKey(false), cursor(NULL)
{
+#if !defined(WIN32) && !defined(__APPLE__)
+ XkbDescPtr xkb;
+ Status status;
+
+ xkb = XkbGetMap(fl_display, 0, XkbUseCoreKbd);
+ if (!xkb)
+ throw Exception("XkbGetMap");
+
+ status = XkbGetNames(fl_display, XkbKeyNamesMask, xkb);
+ if (status != Success)
+ throw Exception("XkbGetNames");
+
+ memset(code_map_keycode_to_qnum, 0, sizeof(code_map_keycode_to_qnum));
+ for (KeyCode keycode = xkb->min_key_code;
+ keycode < xkb->max_key_code;
+ keycode++) {
+ const char *keyname = xkb->names->keys[keycode].name;
+ unsigned short rfbcode;
+
+ if (keyname[0] == '\0')
+ continue;
+
+ rfbcode = 0;
+ for (unsigned i = 0;i < code_map_xkb_to_qnum_len;i++) {
+ if (strncmp(code_map_xkb_to_qnum[i].from,
+ keyname, XkbKeyNameLength) == 0) {
+ rfbcode = code_map_xkb_to_qnum[i].to;
+ break;
+ }
+ }
+ if (rfbcode != 0)
+ code_map_keycode_to_qnum[keycode] = rfbcode;
+ else
+ vlog.debug("No key mapping for key %.4s", keyname);
+ }
+
+ XkbFreeKeyboard(xkb, 0, True);
+#endif
+
Fl::add_clipboard_notify(handleClipboardChange, this);
// We need to intercept keyboard events early
@@ -218,6 +277,189 @@
}
+void Viewport::setLEDState(unsigned int state)
+{
+ Fl_Widget *focus;
+
+ vlog.debug("Got server LED state: 0x%08x", state);
+
+ focus = Fl::grab();
+ if (!focus)
+ focus = Fl::focus();
+ if (!focus)
+ return;
+
+ if (focus != this)
+ return;
+
+#if defined(WIN32)
+ INPUT input[6];
+ UINT count;
+ UINT ret;
+
+ memset(input, 0, sizeof(input));
+ count = 0;
+
+ if (!!(state & ledCapsLock) != !!(GetKeyState(VK_CAPITAL) & 0x1)) {
+ input[count].type = input[count+1].type = INPUT_KEYBOARD;
+ input[count].ki.wVk = input[count+1].ki.wVk = VK_CAPITAL;
+ input[count].ki.wScan = input[count+1].ki.wScan = SCAN_FAKE;
+ input[count].ki.dwFlags = 0;
+ input[count+1].ki.dwFlags = KEYEVENTF_KEYUP;
+ count += 2;
+ }
+
+ if (!!(state & ledNumLock) != !!(GetKeyState(VK_NUMLOCK) & 0x1)) {
+ input[count].type = input[count+1].type = INPUT_KEYBOARD;
+ input[count].ki.wVk = input[count+1].ki.wVk = VK_NUMLOCK;
+ input[count].ki.wScan = input[count+1].ki.wScan = SCAN_FAKE;
+ input[count].ki.dwFlags = KEYEVENTF_EXTENDEDKEY;
+ input[count+1].ki.dwFlags = KEYEVENTF_KEYUP | KEYEVENTF_EXTENDEDKEY;
+ count += 2;
+ }
+
+ if (!!(state & ledScrollLock) != !!(GetKeyState(VK_SCROLL) & 0x1)) {
+ input[count].type = input[count+1].type = INPUT_KEYBOARD;
+ input[count].ki.wVk = input[count+1].ki.wVk = VK_SCROLL;
+ input[count].ki.wScan = input[count+1].ki.wScan = SCAN_FAKE;
+ input[count].ki.dwFlags = 0;
+ input[count+1].ki.dwFlags = KEYEVENTF_KEYUP;
+ count += 2;
+ }
+
+ if (count == 0)
+ return;
+
+ ret = SendInput(count, input, sizeof(*input));
+ if (ret < count)
+ vlog.error(_("Failed to update keyboard LED state: %lu"), GetLastError());
+#elif defined(__APPLE__)
+ int ret;
+
+ ret = cocoa_set_caps_lock_state(state & ledCapsLock);
+ if (ret != 0) {
+ vlog.error(_("Failed to update keyboard LED state: %d"), ret);
+ return;
+ }
+
+ ret = cocoa_set_num_lock_state(state & ledNumLock);
+ if (ret != 0) {
+ vlog.error(_("Failed to update keyboard LED state: %d"), ret);
+ return;
+ }
+
+ // No support for Scroll Lock //
+
+#else
+ unsigned int affect, values;
+ unsigned int mask;
+
+ Bool ret;
+
+ affect = values = 0;
+
+ affect |= LockMask;
+ if (state & ledCapsLock)
+ values |= LockMask;
+
+ mask = getModifierMask(XK_Num_Lock);
+ affect |= mask;
+ if (state & ledNumLock)
+ values |= mask;
+
+ mask = getModifierMask(XK_Scroll_Lock);
+ affect |= mask;
+ if (state & ledScrollLock)
+ values |= mask;
+
+ ret = XkbLockModifiers(fl_display, XkbUseCoreKbd, affect, values);
+ if (!ret)
+ vlog.error(_("Failed to update keyboard LED state"));
+#endif
+}
+
+void Viewport::pushLEDState()
+{
+ unsigned int state;
+
+ // Server support?
+ if (cc->cp.ledState() == ledUnknown)
+ return;
+
+ state = 0;
+
+#if defined(WIN32)
+ if (GetKeyState(VK_CAPITAL) & 0x1)
+ state |= ledCapsLock;
+ if (GetKeyState(VK_NUMLOCK) & 0x1)
+ state |= ledNumLock;
+ if (GetKeyState(VK_SCROLL) & 0x1)
+ state |= ledScrollLock;
+#elif defined(__APPLE__)
+ int ret;
+ bool on;
+
+ ret = cocoa_get_caps_lock_state(&on);
+ if (ret != 0) {
+ vlog.error(_("Failed to get keyboard LED state: %d"), ret);
+ return;
+ }
+ if (on)
+ state |= ledCapsLock;
+
+ ret = cocoa_get_num_lock_state(&on);
+ if (ret != 0) {
+ vlog.error(_("Failed to get keyboard LED state: %d"), ret);
+ return;
+ }
+ if (on)
+ state |= ledNumLock;
+
+ // No support for Scroll Lock //
+ state |= (cc->cp.ledState() & ledScrollLock);
+
+#else
+ unsigned int mask;
+
+ Status status;
+ XkbStateRec xkbState;
+
+ status = XkbGetState(fl_display, XkbUseCoreKbd, &xkbState);
+ if (status != Success) {
+ vlog.error(_("Failed to get keyboard LED state: %d"), status);
+ return;
+ }
+
+ if (xkbState.locked_mods & LockMask)
+ state |= ledCapsLock;
+
+ mask = getModifierMask(XK_Num_Lock);
+ if (xkbState.locked_mods & mask)
+ state |= ledNumLock;
+
+ mask = getModifierMask(XK_Scroll_Lock);
+ if (xkbState.locked_mods & mask)
+ state |= ledScrollLock;
+#endif
+
+ if ((state & ledCapsLock) != (cc->cp.ledState() & ledCapsLock)) {
+ vlog.debug("Inserting fake CapsLock to get in sync with server");
+ handleKeyPress(0x3a, XK_Caps_Lock);
+ handleKeyRelease(0x3a);
+ }
+ if ((state & ledNumLock) != (cc->cp.ledState() & ledNumLock)) {
+ vlog.debug("Inserting fake NumLock to get in sync with server");
+ handleKeyPress(0x45, XK_Num_Lock);
+ handleKeyRelease(0x45);
+ }
+ if ((state & ledScrollLock) != (cc->cp.ledState() & ledScrollLock)) {
+ vlog.debug("Inserting fake ScrollLock to get in sync with server");
+ handleKeyPress(0x46, XK_Scroll_Lock);
+ handleKeyRelease(0x46);
+ }
+}
+
+
void Viewport::draw(Surface* dst)
{
int X, Y, W, H;
@@ -352,6 +594,55 @@
return Fl_Widget::handle(event);
}
+
+#if ! (defined(WIN32) || defined(__APPLE__))
+unsigned int Viewport::getModifierMask(unsigned int keysym)
+{
+ XkbDescPtr xkb;
+ unsigned int mask, keycode;
+ XkbAction *act;
+
+ mask = 0;
+
+ xkb = XkbGetMap(fl_display, XkbAllComponentsMask, XkbUseCoreKbd);
+ if (xkb == NULL)
+ return 0;
+
+ for (keycode = xkb->min_key_code; keycode <= xkb->max_key_code; keycode++) {
+ unsigned int state_out;
+ KeySym ks;
+
+ XkbTranslateKeyCode(xkb, keycode, 0, &state_out, &ks);
+ if (ks == NoSymbol)
+ continue;
+
+ if (ks == keysym)
+ break;
+ }
+
+ // KeySym not mapped?
+ if (keycode > xkb->max_key_code)
+ goto out;
+
+ act = XkbKeyAction(xkb, keycode, 0);
+ if (act == NULL)
+ goto out;
+ if (act->type != XkbSA_LockMods)
+ goto out;
+
+ if (act->mods.flags & XkbSA_UseModMapMods)
+ mask = xkb->map->modmap[keycode];
+ else
+ mask = act->mods.mask;
+
+out:
+ XkbFreeKeyboard(xkb, XkbAllComponentsMask, True);
+
+ return mask;
+}
+#endif
+
+
void Viewport::handleClipboardChange(int source, void *data)
{
Viewport *self = (Viewport *)data;
@@ -422,6 +713,11 @@
if (viewOnly)
return;
+ if (keyCode == 0) {
+ vlog.error(_("No key code specified on key press"));
+ return;
+ }
+
#ifdef __APPLE__
// Alt on OS X behaves more like AltGr on other systems, and to get
// sane behaviour we should translate things in that manner for the
@@ -452,23 +748,11 @@
// Ctrl+Alt+AltGr, which we usually end up with when Xvnc tries to
// get everything in the correct state. Cheat and temporarily release
// Ctrl and Alt when we send some other symbol.
- bool ctrlPressed, altPressed;
- DownMap::iterator iter;
-
- ctrlPressed = false;
- altPressed = false;
- for (iter = downKeySym.begin();iter != downKeySym.end();++iter) {
- if (iter->second == XK_Control_L)
- ctrlPressed = true;
- else if (iter->second == XK_Alt_R)
- altPressed = true;
- }
-
- if (ctrlPressed && altPressed) {
+ if (downKeySym.count(0x1d) && downKeySym.count(0xb8)) {
vlog.debug("Faking release of AltGr (Ctrl_L+Alt_R)");
try {
- cc->writer()->keyEvent(XK_Control_L, false);
- cc->writer()->keyEvent(XK_Alt_R, false);
+ cc->writer()->keyEvent(downKeySym[0x1d], 0x1d, false);
+ cc->writer()->keyEvent(downKeySym[0xb8], 0xb8, false);
} catch (rdr::Exception& e) {
vlog.error("%s", e.str());
exit_vncviewer(e.str());
@@ -490,7 +774,11 @@
#endif
try {
- cc->writer()->keyEvent(keySym, true);
+ // Fake keycode?
+ if (keyCode > 0xff)
+ cc->writer()->keyEvent(keySym, 0, true);
+ else
+ cc->writer()->keyEvent(keySym, keyCode, true);
} catch (rdr::Exception& e) {
vlog.error("%s", e.str());
exit_vncviewer(e.str());
@@ -498,11 +786,11 @@
#ifdef WIN32
// Ugly hack continued...
- if (ctrlPressed && altPressed) {
+ if (downKeySym.count(0x1d) && downKeySym.count(0xb8)) {
vlog.debug("Restoring AltGr state");
try {
- cc->writer()->keyEvent(XK_Control_L, true);
- cc->writer()->keyEvent(XK_Alt_R, true);
+ cc->writer()->keyEvent(downKeySym[0x1d], 0x1d, true);
+ cc->writer()->keyEvent(downKeySym[0xb8], 0xb8, true);
} catch (rdr::Exception& e) {
vlog.error("%s", e.str());
exit_vncviewer(e.str());
@@ -535,7 +823,10 @@
#endif
try {
- cc->writer()->keyEvent(iter->second, false);
+ if (keyCode > 0xff)
+ cc->writer()->keyEvent(iter->second, 0, false);
+ else
+ cc->writer()->keyEvent(iter->second, keyCode, false);
} catch (rdr::Exception& e) {
vlog.error("%s", e.str());
exit_vncviewer(e.str());
@@ -577,6 +868,11 @@
keyCode = ((msg->lParam >> 16) & 0xff);
+ if (keyCode == SCAN_FAKE) {
+ vlog.debug("Ignoring fake key press (virtual key 0x%02x)", vKey);
+ return 1;
+ }
+
// Windows sets the scan code to 0x00 for multimedia keys, so we
// have to do a reverse lookup based on the vKey.
if (keyCode == 0x00) {
@@ -590,14 +886,19 @@
}
}
+ if (keyCode & ~0x7f) {
+ vlog.error(_("Invalid scan code 0x%02x"), (int)keyCode);
+ return 1;
+ }
+
if (isExtended)
- keyCode |= 0x100;
+ keyCode |= 0x80;
// VK_SNAPSHOT sends different scan codes depending on the state of
// Alt. This means that we can get different scan codes on press and
// release. Force it to be something standard.
if (vKey == VK_SNAPSHOT)
- keyCode = 0x137;
+ keyCode = 0x54;
keySym = win32_vkey_to_keysym(vKey, isExtended);
if (keySym == NoSymbol) {
@@ -605,9 +906,12 @@
vlog.error(_("No symbol for extended virtual key 0x%02x"), (int)vKey);
else
vlog.error(_("No symbol for virtual key 0x%02x"), (int)vKey);
- return 1;
}
+ // Fortunately RFB and Windows use the same scan code set,
+ // so there is no conversion needed
+ // (as long as we encode the extended keys with the high bit)
+
self->handleKeyPress(keyCode, keySym);
return 1;
@@ -620,12 +924,18 @@
isExtended = (msg->lParam & (1 << 24)) != 0;
keyCode = ((msg->lParam >> 16) & 0xff);
+
+ if (keyCode == SCAN_FAKE) {
+ vlog.debug("Ignoring fake key release (virtual key 0x%02x)", vKey);
+ return 1;
+ }
+
if (keyCode == 0x00)
keyCode = MapVirtualKey(vKey, MAPVK_VK_TO_VSC);
if (isExtended)
- keyCode |= 0x100;
+ keyCode |= 0x80;
if (vKey == VK_SNAPSHOT)
- keyCode = 0x137;
+ keyCode = 0x54;
self->handleKeyRelease(keyCode);
@@ -636,6 +946,10 @@
int keyCode;
keyCode = cocoa_event_keycode(event);
+ if ((unsigned)keyCode >= code_map_osx_to_qnum_len)
+ keyCode = 0;
+ else
+ keyCode = code_map_osx_to_qnum[keyCode];
if (cocoa_is_key_press(event)) {
rdr::U32 keySym;
@@ -644,7 +958,6 @@
if (keySym == NoSymbol) {
vlog.error(_("No symbol for key code 0x%02x (in the current state)"),
(int)keyCode);
- return 1;
}
self->handleKeyPress(keyCode, keySym);
@@ -663,14 +976,21 @@
XEvent *xevent = (XEvent*)event;
if (xevent->type == KeyPress) {
+ int keycode;
char str;
KeySym keysym;
+ keycode = code_map_keycode_to_qnum[xevent->xkey.keycode];
+
+ // Generate a fake keycode just for tracking if we can't figure
+ // out the proper one
+ if (keycode == 0)
+ keycode = 0x100 | xevent->xkey.keycode;
+
XLookupString(&xevent->xkey, &str, 1, &keysym, NULL);
if (keysym == NoSymbol) {
vlog.error(_("No symbol for key code %d (in the current state)"),
(int)xevent->xkey.keycode);
- return 1;
}
switch (keysym) {
@@ -690,10 +1010,13 @@
break;
}
- self->handleKeyPress(xevent->xkey.keycode, keysym);
+ self->handleKeyPress(keycode, keysym);
return 1;
} else if (xevent->type == KeyRelease) {
- self->handleKeyRelease(xevent->xkey.keycode);
+ int keycode = code_map_keycode_to_qnum[xevent->xkey.keycode];
+ if (keycode == 0)
+ keycode = 0x100 | xevent->xkey.keycode;
+ self->handleKeyRelease(keycode);
return 1;
}
#endif
@@ -729,7 +1052,7 @@
char sendMenuKey[64];
snprintf(sendMenuKey, 64, p_("ContextMenu|", "Send %s"), (const char *)menuKey);
fltk_menu_add(contextMenu, sendMenuKey, 0, NULL, (void*)ID_MENUKEY, 0);
- fltk_menu_add(contextMenu, "Secret shortcut menu key", menuKeyCode, NULL,
+ fltk_menu_add(contextMenu, "Secret shortcut menu key", menuKeyFLTK, NULL,
(void*)ID_MENUKEY, FL_MENU_INVISIBLE);
}
@@ -797,30 +1120,30 @@
break;
case ID_CTRL:
if (m->value())
- handleKeyPress(fakeKeyBase + 0, XK_Control_L);
+ handleKeyPress(0x1d, XK_Control_L);
else
- handleKeyRelease(fakeKeyBase + 0);
+ handleKeyRelease(0x1d);
menuCtrlKey = !menuCtrlKey;
break;
case ID_ALT:
if (m->value())
- handleKeyPress(fakeKeyBase + 1, XK_Alt_L);
+ handleKeyPress(0x38, XK_Alt_L);
else
- handleKeyRelease(fakeKeyBase + 1);
+ handleKeyRelease(0x38);
menuAltKey = !menuAltKey;
break;
case ID_MENUKEY:
- handleKeyPress(fakeKeyBase + 2, menuKeySym);
- handleKeyRelease(fakeKeyBase + 2);
+ handleKeyPress(menuKeyCode, menuKeySym);
+ handleKeyRelease(menuKeyCode);
break;
case ID_CTRLALTDEL:
- handleKeyPress(fakeKeyBase + 3, XK_Control_L);
- handleKeyPress(fakeKeyBase + 4, XK_Alt_L);
- handleKeyPress(fakeKeyBase + 5, XK_Delete);
+ handleKeyPress(0x1d, XK_Control_L);
+ handleKeyPress(0x38, XK_Alt_L);
+ handleKeyPress(0xd3, XK_Delete);
- handleKeyRelease(fakeKeyBase + 5);
- handleKeyRelease(fakeKeyBase + 4);
- handleKeyRelease(fakeKeyBase + 3);
+ handleKeyRelease(0xd3);
+ handleKeyRelease(0x38);
+ handleKeyRelease(0x1d);
break;
case ID_REFRESH:
cc->refreshFramebuffer();
@@ -846,7 +1169,7 @@
void Viewport::setMenuKey()
{
- getMenuKey(&menuKeyCode, &menuKeySym);
+ getMenuKey(&menuKeyFLTK, &menuKeyCode, &menuKeySym);
}
diff --git a/vncviewer/Viewport.h b/vncviewer/Viewport.h
index 6f0710d..a4b7d8b 100644
--- a/vncviewer/Viewport.h
+++ b/vncviewer/Viewport.h
@@ -47,6 +47,11 @@
void setCursor(int width, int height, const rfb::Point& hotspot,
const rdr::U8* data);
+ // Change client LED state
+ void setLEDState(unsigned int state);
+ // Change server LED state
+ void pushLEDState();
+
void draw(Surface* dst);
// Fl_Widget callback methods
@@ -59,6 +64,8 @@
private:
+ unsigned int getModifierMask(unsigned int keysym);
+
static void handleClipboardChange(int source, void *data);
void handlePointerEvent(const rfb::Point& pos, int buttonMask);
@@ -88,7 +95,7 @@
DownMap downKeySym;
rdr::U32 menuKeySym;
- int menuKeyCode;
+ int menuKeyCode, menuKeyFLTK;
Fl_Menu_Button *contextMenu;
bool menuCtrlKey;
diff --git a/vncviewer/cocoa.h b/vncviewer/cocoa.h
index 0c3ac82..a713802 100644
--- a/vncviewer/cocoa.h
+++ b/vncviewer/cocoa.h
@@ -33,4 +33,10 @@
int cocoa_event_keycode(const void *event);
int cocoa_event_keysym(const void *event);
+int cocoa_set_caps_lock_state(bool on);
+int cocoa_set_num_lock_state(bool on);
+
+int cocoa_get_caps_lock_state(bool *on);
+int cocoa_get_num_lock_state(bool *on);
+
#endif
diff --git a/vncviewer/cocoa.mm b/vncviewer/cocoa.mm
index 6e464fa..e26851d 100644
--- a/vncviewer/cocoa.mm
+++ b/vncviewer/cocoa.mm
@@ -27,6 +27,9 @@
#import <Cocoa/Cocoa.h>
#import <Carbon/Carbon.h>
+#include <IOKit/hidsystem/IOHIDLib.h>
+#include <IOKit/hidsystem/IOHIDParameter.h>
+
#define XK_LATIN1
#define XK_MISCELLANY
#define XK_XKB_KEYS
@@ -406,3 +409,77 @@
return ucs2keysym([chars characterAtIndex:0]);
}
+
+static int cocoa_open_hid(io_connect_t *ioc)
+{
+ kern_return_t ret;
+ io_service_t ios;
+ CFMutableDictionaryRef mdict;
+
+ mdict = IOServiceMatching(kIOHIDSystemClass);
+ ios = IOServiceGetMatchingService(kIOMasterPortDefault,
+ (CFDictionaryRef) mdict);
+ if (!ios)
+ return KERN_FAILURE;
+
+ ret = IOServiceOpen(ios, mach_task_self(), kIOHIDParamConnectType, ioc);
+ IOObjectRelease(ios);
+ if (ret != KERN_SUCCESS)
+ return ret;
+
+ return KERN_SUCCESS;
+}
+
+static int cocoa_set_modifier_lock_state(int modifier, bool on)
+{
+ kern_return_t ret;
+ io_connect_t ioc;
+
+ ret = cocoa_open_hid(&ioc);
+ if (ret != KERN_SUCCESS)
+ return ret;
+
+ ret = IOHIDSetModifierLockState(ioc, modifier, on);
+ IOServiceClose(ioc);
+ if (ret != KERN_SUCCESS)
+ return ret;
+
+ return KERN_SUCCESS;
+}
+
+static int cocoa_get_modifier_lock_state(int modifier, bool *on)
+{
+ kern_return_t ret;
+ io_connect_t ioc;
+
+ ret = cocoa_open_hid(&ioc);
+ if (ret != KERN_SUCCESS)
+ return ret;
+
+ ret = IOHIDGetModifierLockState(ioc, modifier, on);
+ IOServiceClose(ioc);
+ if (ret != KERN_SUCCESS)
+ return ret;
+
+ return KERN_SUCCESS;
+}
+
+int cocoa_set_caps_lock_state(bool on)
+{
+ return cocoa_set_modifier_lock_state(kIOHIDCapsLockState, on);
+}
+
+int cocoa_set_num_lock_state(bool on)
+{
+ return cocoa_set_modifier_lock_state(kIOHIDNumLockState, on);
+}
+
+int cocoa_get_caps_lock_state(bool *on)
+{
+ return cocoa_get_modifier_lock_state(kIOHIDCapsLockState, on);
+}
+
+int cocoa_get_num_lock_state(bool *on)
+{
+ return cocoa_get_modifier_lock_state(kIOHIDNumLockState, on);
+}
diff --git a/vncviewer/menukey.cxx b/vncviewer/menukey.cxx
index 04e52d0..25f9f56 100644
--- a/vncviewer/menukey.cxx
+++ b/vncviewer/menukey.cxx
@@ -30,26 +30,26 @@
#include "parameters.h"
static const MenuKeySymbol menuSymbols[] = {
- {"F1", FL_F + 1, XK_F1},
- {"F2", FL_F + 2, XK_F2},
- {"F3", FL_F + 3, XK_F3},
- {"F4", FL_F + 4, XK_F4},
- {"F5", FL_F + 5, XK_F5},
- {"F6", FL_F + 6, XK_F6},
- {"F7", FL_F + 7, XK_F7},
- {"F8", FL_F + 8, XK_F8},
- {"F9", FL_F + 9, XK_F9},
- {"F10", FL_F + 10, XK_F10},
- {"F11", FL_F + 11, XK_F11},
- {"F12", FL_F + 12, XK_F12},
- {"Pause", FL_Pause, XK_Pause},
- {"Scroll_Lock", FL_Scroll_Lock, XK_Scroll_Lock},
- {"Escape", FL_Escape, XK_Escape},
- {"Insert", FL_Insert, XK_Insert},
- {"Delete", FL_Delete, XK_Delete},
- {"Home", FL_Home, XK_Home},
- {"Page_Up", FL_Page_Up, XK_Page_Up},
- {"Page_Down", FL_Page_Down, XK_Page_Down},
+ {"F1", FL_F + 1, 0x3b, XK_F1},
+ {"F2", FL_F + 2, 0x3c, XK_F2},
+ {"F3", FL_F + 3, 0x3d, XK_F3},
+ {"F4", FL_F + 4, 0x3e, XK_F4},
+ {"F5", FL_F + 5, 0x3f, XK_F5},
+ {"F6", FL_F + 6, 0x40, XK_F6},
+ {"F7", FL_F + 7, 0x41, XK_F7},
+ {"F8", FL_F + 8, 0x42, XK_F8},
+ {"F9", FL_F + 9, 0x43, XK_F9},
+ {"F10", FL_F + 10, 0x44, XK_F10},
+ {"F11", FL_F + 11, 0x57, XK_F11},
+ {"F12", FL_F + 12, 0x58, XK_F12},
+ {"Pause", FL_Pause, 0xc6, XK_Pause},
+ {"Scroll_Lock", FL_Scroll_Lock, 0x46, XK_Scroll_Lock},
+ {"Escape", FL_Escape, 0x01, XK_Escape},
+ {"Insert", FL_Insert, 0xd2, XK_Insert},
+ {"Delete", FL_Delete, 0xd3, XK_Delete},
+ {"Home", FL_Home, 0xc7, XK_Home},
+ {"Page_Up", FL_Page_Up, 0xc9, XK_Page_Up},
+ {"Page_Down", FL_Page_Down, 0xd1, XK_Page_Down},
};
int getMenuKeySymbolCount()
@@ -62,19 +62,21 @@
return menuSymbols;
}
-void getMenuKey(int *keycode, rdr::U32 *keysym)
+void getMenuKey(int *fltkcode, int *keycode, rdr::U32 *keysym)
{
const char *menuKeyStr;
menuKeyStr = menuKey;
for(int i = 0; i < getMenuKeySymbolCount(); i++) {
if (!strcmp(menuSymbols[i].name, menuKeyStr)) {
+ *fltkcode = menuSymbols[i].fltkcode;
*keycode = menuSymbols[i].keycode;
*keysym = menuSymbols[i].keysym;
return;
}
}
+ *fltkcode = 0;
*keycode = 0;
*keysym = 0;
}
diff --git a/vncviewer/menukey.h b/vncviewer/menukey.h
index fcc5135..7935428 100644
--- a/vncviewer/menukey.h
+++ b/vncviewer/menukey.h
@@ -22,11 +22,12 @@
typedef struct {
const char* name;
+ int fltkcode;
int keycode;
rdr::U32 keysym;
} MenuKeySymbol;
-void getMenuKey(int *keycode, rdr::U32 *keysym);
+void getMenuKey(int *fltkcode, int *keycode, rdr::U32 *keysym);
int getMenuKeySymbolCount();
const MenuKeySymbol* getMenuKeySymbols();
diff --git a/vncviewer/osx_to_qnum.c b/vncviewer/osx_to_qnum.c
new file mode 100644
index 0000000..511b7d5
--- /dev/null
+++ b/vncviewer/osx_to_qnum.c
@@ -0,0 +1,126 @@
+/*
+ * This file is auto-generated from keymaps.csv on 2017-08-28 13:04
+ * Database checksum sha256(f8aeff0c3430077a350e3d7ba2b335b381bd929ac4b193413730a402ff3f0097)
+ * To re-generate, run:
+ * keymap-gen --lang=stdc code-map keymaps.csv osx qnum
+*/
+const unsigned short code_map_osx_to_qnum[256] = {
+ [0x0] = 0x1e, /* osx:0 (ANSI_A) -> linux:30 (KEY_A) -> qnum:30 */
+ [0x1] = 0x1f, /* osx:1 (ANSI_S) -> linux:31 (KEY_S) -> qnum:31 */
+ [0x2] = 0x20, /* osx:2 (ANSI_D) -> linux:32 (KEY_D) -> qnum:32 */
+ [0x3] = 0x21, /* osx:3 (ANSI_F) -> linux:33 (KEY_F) -> qnum:33 */
+ [0x4] = 0x23, /* osx:4 (ANSI_H) -> linux:35 (KEY_H) -> qnum:35 */
+ [0x5] = 0x22, /* osx:5 (ANSI_G) -> linux:34 (KEY_G) -> qnum:34 */
+ [0x6] = 0x2c, /* osx:6 (ANSI_Z) -> linux:44 (KEY_Z) -> qnum:44 */
+ [0x7] = 0x2d, /* osx:7 (ANSI_X) -> linux:45 (KEY_X) -> qnum:45 */
+ [0x8] = 0x2e, /* osx:8 (ANSI_C) -> linux:46 (KEY_C) -> qnum:46 */
+ [0x9] = 0x2f, /* osx:9 (ANSI_V) -> linux:47 (KEY_V) -> qnum:47 */
+ [0xa] = 0x70, /* osx:10 (ISO_Section) -> linux:170 (KEY_ISO) -> qnum:112 */
+ [0xb] = 0x30, /* osx:11 (ANSI_B) -> linux:48 (KEY_B) -> qnum:48 */
+ [0xc] = 0x10, /* osx:12 (ANSI_Q) -> linux:16 (KEY_Q) -> qnum:16 */
+ [0xd] = 0x11, /* osx:13 (ANSI_W) -> linux:17 (KEY_W) -> qnum:17 */
+ [0xe] = 0x12, /* osx:14 (ANSI_E) -> linux:18 (KEY_E) -> qnum:18 */
+ [0xf] = 0x13, /* osx:15 (ANSI_R) -> linux:19 (KEY_R) -> qnum:19 */
+ [0x10] = 0x15, /* osx:16 (ANSI_Y) -> linux:21 (KEY_Y) -> qnum:21 */
+ [0x11] = 0x14, /* osx:17 (ANSI_T) -> linux:20 (KEY_T) -> qnum:20 */
+ [0x12] = 0x2, /* osx:18 (ANSI_1) -> linux:2 (KEY_1) -> qnum:2 */
+ [0x13] = 0x3, /* osx:19 (ANSI_2) -> linux:3 (KEY_2) -> qnum:3 */
+ [0x14] = 0x4, /* osx:20 (ANSI_3) -> linux:4 (KEY_3) -> qnum:4 */
+ [0x15] = 0x5, /* osx:21 (ANSI_4) -> linux:5 (KEY_4) -> qnum:5 */
+ [0x16] = 0x7, /* osx:22 (ANSI_6) -> linux:7 (KEY_6) -> qnum:7 */
+ [0x17] = 0x6, /* osx:23 (ANSI_5) -> linux:6 (KEY_5) -> qnum:6 */
+ [0x18] = 0xd, /* osx:24 (ANSI_Equal) -> linux:13 (KEY_EQUAL) -> qnum:13 */
+ [0x19] = 0xa, /* osx:25 (ANSI_9) -> linux:10 (KEY_9) -> qnum:10 */
+ [0x1a] = 0x8, /* osx:26 (ANSI_7) -> linux:8 (KEY_7) -> qnum:8 */
+ [0x1b] = 0xc, /* osx:27 (ANSI_Minus) -> linux:12 (KEY_MINUS) -> qnum:12 */
+ [0x1c] = 0x9, /* osx:28 (ANSI_8) -> linux:9 (KEY_8) -> qnum:9 */
+ [0x1d] = 0xb, /* osx:29 (ANSI_0) -> linux:11 (KEY_0) -> qnum:11 */
+ [0x1e] = 0x1b, /* osx:30 (ANSI_RightBracket) -> linux:27 (KEY_RIGHTBRACE) -> qnum:27 */
+ [0x1f] = 0x18, /* osx:31 (ANSI_O) -> linux:24 (KEY_O) -> qnum:24 */
+ [0x20] = 0x16, /* osx:32 (ANSI_U) -> linux:22 (KEY_U) -> qnum:22 */
+ [0x21] = 0x1a, /* osx:33 (ANSI_LeftBracket) -> linux:26 (KEY_LEFTBRACE) -> qnum:26 */
+ [0x22] = 0x17, /* osx:34 (ANSI_I) -> linux:23 (KEY_I) -> qnum:23 */
+ [0x23] = 0x19, /* osx:35 (ANSI_P) -> linux:25 (KEY_P) -> qnum:25 */
+ [0x24] = 0x1c, /* osx:36 (Return) -> linux:28 (KEY_ENTER) -> qnum:28 */
+ [0x25] = 0x26, /* osx:37 (ANSI_L) -> linux:38 (KEY_L) -> qnum:38 */
+ [0x26] = 0x24, /* osx:38 (ANSI_J) -> linux:36 (KEY_J) -> qnum:36 */
+ [0x27] = 0x28, /* osx:39 (ANSI_Quote) -> linux:40 (KEY_APOSTROPHE) -> qnum:40 */
+ [0x28] = 0x25, /* osx:40 (ANSI_K) -> linux:37 (KEY_K) -> qnum:37 */
+ [0x29] = 0x27, /* osx:41 (ANSI_Semicolon) -> linux:39 (KEY_SEMICOLON) -> qnum:39 */
+ [0x2a] = 0x2b, /* osx:42 (ANSI_Backslash) -> linux:43 (KEY_BACKSLASH) -> qnum:43 */
+ [0x2b] = 0x33, /* osx:43 (ANSI_Comma) -> linux:51 (KEY_COMMA) -> qnum:51 */
+ [0x2c] = 0x35, /* osx:44 (ANSI_Slash) -> linux:53 (KEY_SLASH) -> qnum:53 */
+ [0x2d] = 0x31, /* osx:45 (ANSI_N) -> linux:49 (KEY_N) -> qnum:49 */
+ [0x2e] = 0x32, /* osx:46 (ANSI_M) -> linux:50 (KEY_M) -> qnum:50 */
+ [0x2f] = 0x34, /* osx:47 (ANSI_Period) -> linux:52 (KEY_DOT) -> qnum:52 */
+ [0x30] = 0xf, /* osx:48 (Tab) -> linux:15 (KEY_TAB) -> qnum:15 */
+ [0x31] = 0x39, /* osx:49 (Space) -> linux:57 (KEY_SPACE) -> qnum:57 */
+ [0x32] = 0x29, /* osx:50 (ANSI_Grave) -> linux:41 (KEY_GRAVE) -> qnum:41 */
+ [0x33] = 0xe, /* osx:51 (Delete) -> linux:14 (KEY_BACKSPACE) -> qnum:14 */
+ [0x35] = 0x1, /* osx:53 (Escape) -> linux:1 (KEY_ESC) -> qnum:1 */
+ [0x36] = 0xdc, /* osx:54 (RightCommand) -> linux:126 (KEY_RIGHTMETA) -> qnum:220 */
+ [0x37] = 0xdb, /* osx:55 (Command) -> linux:125 (KEY_LEFTMETA) -> qnum:219 */
+ [0x38] = 0x2a, /* osx:56 (Shift) -> linux:42 (KEY_LEFTSHIFT) -> qnum:42 */
+ [0x39] = 0x3a, /* osx:57 (CapsLock) -> linux:58 (KEY_CAPSLOCK) -> qnum:58 */
+ [0x3a] = 0x38, /* osx:58 (Option) -> linux:56 (KEY_LEFTALT) -> qnum:56 */
+ [0x3b] = 0x1d, /* osx:59 (Control) -> linux:29 (KEY_LEFTCTRL) -> qnum:29 */
+ [0x3c] = 0x36, /* osx:60 (RightShift) -> linux:54 (KEY_RIGHTSHIFT) -> qnum:54 */
+ [0x3d] = 0xb8, /* osx:61 (RightOption) -> linux:100 (KEY_RIGHTALT) -> qnum:184 */
+ [0x3e] = 0x9d, /* osx:62 (RightControl) -> linux:97 (KEY_RIGHTCTRL) -> qnum:157 */
+ [0x40] = 0x83, /* osx:64 (F17) -> linux:187 (KEY_F17) -> qnum:131 */
+ [0x41] = 0x53, /* osx:65 (ANSI_KeypadDecimal) -> linux:83 (KEY_KPDOT) -> qnum:83 */
+ [0x43] = 0x37, /* osx:67 (ANSI_KeypadMultiply) -> linux:55 (KEY_KPASTERISK) -> qnum:55 */
+ [0x45] = 0x4e, /* osx:69 (ANSI_KeypadPlus) -> linux:78 (KEY_KPPLUS) -> qnum:78 */
+ [0x47] = 0x45, /* osx:71 (ANSI_KeypadClear) -> linux:69 (KEY_NUMLOCK) -> qnum:69 */
+ [0x48] = 0xb0, /* osx:72 (VolumeUp) -> linux:115 (KEY_VOLUMEUP) -> qnum:176 */
+ [0x49] = 0xae, /* osx:73 (VolumeDown) -> linux:114 (KEY_VOLUMEDOWN) -> qnum:174 */
+ [0x4a] = 0xa0, /* osx:74 (Mute) -> linux:113 (KEY_MUTE) -> qnum:160 */
+ [0x4b] = 0xb5, /* osx:75 (ANSI_KeypadDivide) -> linux:98 (KEY_KPSLASH) -> qnum:181 */
+ [0x4c] = 0x9c, /* osx:76 (ANSI_KeypadEnter) -> linux:96 (KEY_KPENTER) -> qnum:156 */
+ [0x4e] = 0x4a, /* osx:78 (ANSI_KeypadMinus) -> linux:74 (KEY_KPMINUS) -> qnum:74 */
+ [0x4f] = 0xf7, /* osx:79 (F18) -> linux:188 (KEY_F18) -> qnum:247 */
+ [0x50] = 0x84, /* osx:80 (F19) -> linux:189 (KEY_F19) -> qnum:132 */
+ [0x51] = 0x59, /* osx:81 (ANSI_KeypadEquals) -> linux:117 (KEY_KPEQUAL) -> qnum:89 */
+ [0x52] = 0x52, /* osx:82 (ANSI_Keypad0) -> linux:82 (KEY_KP0) -> qnum:82 */
+ [0x53] = 0x4f, /* osx:83 (ANSI_Keypad1) -> linux:79 (KEY_KP1) -> qnum:79 */
+ [0x54] = 0x50, /* osx:84 (ANSI_Keypad2) -> linux:80 (KEY_KP2) -> qnum:80 */
+ [0x55] = 0x51, /* osx:85 (ANSI_Keypad3) -> linux:81 (KEY_KP3) -> qnum:81 */
+ [0x56] = 0x4b, /* osx:86 (ANSI_Keypad4) -> linux:75 (KEY_KP4) -> qnum:75 */
+ [0x57] = 0x4c, /* osx:87 (ANSI_Keypad5) -> linux:76 (KEY_KP5) -> qnum:76 */
+ [0x58] = 0x4d, /* osx:88 (ANSI_Keypad6) -> linux:77 (KEY_KP6) -> qnum:77 */
+ [0x59] = 0x47, /* osx:89 (ANSI_Keypad7) -> linux:71 (KEY_KP7) -> qnum:71 */
+ [0x5a] = 0x5a, /* osx:90 (F20) -> linux:190 (KEY_F20) -> qnum:90 */
+ [0x5b] = 0x48, /* osx:91 (ANSI_Keypad8) -> linux:72 (KEY_KP8) -> qnum:72 */
+ [0x5c] = 0x49, /* osx:92 (ANSI_Keypad9) -> linux:73 (KEY_KP9) -> qnum:73 */
+ [0x5d] = 0x7d, /* osx:93 (JIS_Yen) -> linux:124 (KEY_YEN) -> qnum:125 */
+ [0x5f] = 0x5c, /* osx:95 (JIS_KeypadComma) -> linux:95 (KEY_KPJPCOMMA) -> qnum:92 */
+ [0x60] = 0x3f, /* osx:96 (F5) -> linux:63 (KEY_F5) -> qnum:63 */
+ [0x61] = 0x40, /* osx:97 (F6) -> linux:64 (KEY_F6) -> qnum:64 */
+ [0x62] = 0x41, /* osx:98 (F7) -> linux:65 (KEY_F7) -> qnum:65 */
+ [0x63] = 0x3d, /* osx:99 (F3) -> linux:61 (KEY_F3) -> qnum:61 */
+ [0x64] = 0x42, /* osx:100 (F8) -> linux:66 (KEY_F8) -> qnum:66 */
+ [0x65] = 0x43, /* osx:101 (F9) -> linux:67 (KEY_F9) -> qnum:67 */
+ [0x67] = 0x57, /* osx:103 (F11) -> linux:87 (KEY_F11) -> qnum:87 */
+ [0x68] = 0x78, /* osx:104 (JIS_Kana) -> linux:90 (KEY_KATAKANA) -> qnum:120 */
+ [0x69] = 0x5d, /* osx:105 (F13) -> linux:183 (KEY_F13) -> qnum:93 */
+ [0x6a] = 0x55, /* osx:106 (F16) -> linux:186 (KEY_F16) -> qnum:85 */
+ [0x6b] = 0x5e, /* osx:107 (F14) -> linux:184 (KEY_F14) -> qnum:94 */
+ [0x6d] = 0x44, /* osx:109 (F10) -> linux:68 (KEY_F10) -> qnum:68 */
+ [0x6e] = 0xdd, /* osx:110 (unnamed) -> linux:127 (KEY_COMPOSE) -> qnum:221 */
+ [0x6f] = 0x58, /* osx:111 (F12) -> linux:88 (KEY_F12) -> qnum:88 */
+ [0x71] = 0x5f, /* osx:113 (F15) -> linux:185 (KEY_F15) -> qnum:95 */
+ [0x72] = 0xf5, /* osx:114 (Help) -> linux:138 (KEY_HELP) -> qnum:245 */
+ [0x73] = 0xc7, /* osx:115 (Home) -> linux:102 (KEY_HOME) -> qnum:199 */
+ [0x74] = 0xc9, /* osx:116 (PageUp) -> linux:104 (KEY_PAGEUP) -> qnum:201 */
+ [0x75] = 0xd3, /* osx:117 (ForwardDelete) -> linux:111 (KEY_DELETE) -> qnum:211 */
+ [0x76] = 0x3e, /* osx:118 (F4) -> linux:62 (KEY_F4) -> qnum:62 */
+ [0x77] = 0xcf, /* osx:119 (End) -> linux:107 (KEY_END) -> qnum:207 */
+ [0x78] = 0x3c, /* osx:120 (F2) -> linux:60 (KEY_F2) -> qnum:60 */
+ [0x79] = 0xd1, /* osx:121 (PageDown) -> linux:109 (KEY_PAGEDOWN) -> qnum:209 */
+ [0x7a] = 0x3b, /* osx:122 (F1) -> linux:59 (KEY_F1) -> qnum:59 */
+ [0x7b] = 0xcb, /* osx:123 (LeftArrow) -> linux:105 (KEY_LEFT) -> qnum:203 */
+ [0x7c] = 0xcd, /* osx:124 (RightArrow) -> linux:106 (KEY_RIGHT) -> qnum:205 */
+ [0x7d] = 0xd0, /* osx:125 (DownArrow) -> linux:108 (KEY_DOWN) -> qnum:208 */
+ [0x7e] = 0xc8, /* osx:126 (UpArrow) -> linux:103 (KEY_UP) -> qnum:200 */
+};
+const unsigned int code_map_osx_to_qnum_len = sizeof(code_map_osx_to_qnum)/sizeof(code_map_osx_to_qnum[0]);
diff --git a/vncviewer/parameters.cxx b/vncviewer/parameters.cxx
index ad82f27..39a7f07 100644
--- a/vncviewer/parameters.cxx
+++ b/vncviewer/parameters.cxx
@@ -54,11 +54,15 @@
IntParameter pointerEventInterval("PointerEventInterval",
"Time in milliseconds to rate-limit"
- " successive pointer events", 0);
+ " successive pointer events", 17);
BoolParameter dotWhenNoCursor("DotWhenNoCursor",
"Show the dot cursor when the server sends an "
"invisible cursor", false);
+BoolParameter alertOnFatalError("AlertOnFatalError",
+ "Give a dialog on connection problems rather "
+ "than exiting immediately", true);
+
StringParameter passwordFile("PasswordFile",
"Password file for VNC authentication", "");
AliasParameter passwd("passwd", "Alias for PasswordFile", &passwordFile);
@@ -174,7 +178,8 @@
&sendPrimary,
#endif
&menuKey,
- &fullscreenSystemKeys
+ &fullscreenSystemKeys,
+ &alertOnFatalError
};
// Encoding Table
diff --git a/vncviewer/parameters.h b/vncviewer/parameters.h
index 682b6d6..9ce56b9 100644
--- a/vncviewer/parameters.h
+++ b/vncviewer/parameters.h
@@ -60,6 +60,7 @@
extern rfb::StringParameter menuKey;
extern rfb::BoolParameter fullscreenSystemKeys;
+extern rfb::BoolParameter alertOnFatalError;
#ifndef WIN32
extern rfb::StringParameter via;
diff --git a/vncviewer/vncviewer.cxx b/vncviewer/vncviewer.cxx
index de2b423..0ca46af 100644
--- a/vncviewer/vncviewer.cxx
+++ b/vncviewer/vncviewer.cxx
@@ -494,7 +494,9 @@
defaultServerName = loadViewerParameters(NULL);
} catch (rfb::Exception& e) {
defaultServerName = "";
- fl_alert("%s", e.str());
+ vlog.error("%s", e.str());
+ if (alertOnFatalError)
+ fl_alert("%s", e.str());
}
int i = 1;
@@ -532,7 +534,8 @@
// TRANSLATORS: "Parameters" are command line arguments, or settings
// from a file or the Windows registry.
vlog.error(_("Parameters -listen and -via are incompatible"));
- fl_alert(_("Parameters -listen and -via are incompatible"));
+ if (alertOnFatalError)
+ fl_alert(_("Parameters -listen and -via are incompatible"));
exit_vncviewer();
return 1;
}
@@ -580,7 +583,8 @@
}
} catch (rdr::Exception& e) {
vlog.error("%s", e.str());
- fl_alert("%s", e.str());
+ if (alertOnFatalError)
+ fl_alert("%s", e.str());
exit_vncviewer();
return 1;
}
@@ -609,7 +613,7 @@
delete cc;
- if (exitError != NULL)
+ if (exitError != NULL && alertOnFatalError)
fl_alert("%s", exitError);
return 0;
diff --git a/vncviewer/vncviewer.desktop.in b/vncviewer/vncviewer.desktop.in.in
similarity index 70%
rename from vncviewer/vncviewer.desktop.in
rename to vncviewer/vncviewer.desktop.in.in
index 32184b4..d775dde 100644
--- a/vncviewer/vncviewer.desktop.in
+++ b/vncviewer/vncviewer.desktop.in.in
@@ -1,8 +1,7 @@
[Desktop Entry]
Name=TigerVNC Viewer
-Name[fr]=Visionneur TigerVNC
+GenericName=Remote Desktop Viewer
Comment=Connect to VNC server and display remote desktop
-Comment[fr]=Se connecter à un serveur VNC et afficher le bureau distant
Exec=@BIN_DIR@/vncviewer
Icon=tigervnc
Terminal=false
diff --git a/vncviewer/vncviewer.man b/vncviewer/vncviewer.man
index b1450d1..22b46cd 100644
--- a/vncviewer/vncviewer.man
+++ b/vncviewer/vncviewer.man
@@ -243,7 +243,7 @@
.TP
.B \-PointerEventInterval \fItime\fP
Time in milliseconds to rate-limit successive pointer events. Default is
-to send events immediately.
+17 ms (60 Hz).
.
.TP
.B \-Log \fIlogname\fP:\fIdest\fP:\fIlevel\fP
@@ -275,6 +275,10 @@
\fIR\fR, and \fIG\fR taken the values of the local port number, the remote
host, the port number on the remote host, and the gateway machine
respectively.
+.
+.TP
+.B \-AlertOnFatalError
+Display a dialog with any fatal error before exiting. Default is on.
.SH FILES
.TP
diff --git a/vncviewer/xkb_to_qnum.c b/vncviewer/xkb_to_qnum.c
new file mode 100644
index 0000000..759c3d4
--- /dev/null
+++ b/vncviewer/xkb_to_qnum.c
@@ -0,0 +1,257 @@
+/*
+ * This file is auto-generated from keymaps.csv on 2017-08-28 13:04
+ * Database checksum sha256(f8aeff0c3430077a350e3d7ba2b335b381bd929ac4b193413730a402ff3f0097)
+ * To re-generate, run:
+ * keymap-gen --lang=stdc code-map keymaps.csv xkb qnum
+*/
+const struct _code_map_xkb_to_qnum {
+ const char * from;
+ const unsigned short to;
+} code_map_xkb_to_qnum[] = {
+ {"AB00", 0x29}, /* xkb:AB00 (AB00) -> linux:41 (KEY_GRAVE) -> qnum:41 */
+ {"AB01", 0x2c}, /* xkb:AB01 (AB01) -> linux:44 (KEY_Z) -> qnum:44 */
+ {"AB02", 0x2d}, /* xkb:AB02 (AB02) -> linux:45 (KEY_X) -> qnum:45 */
+ {"AB03", 0x2e}, /* xkb:AB03 (AB03) -> linux:46 (KEY_C) -> qnum:46 */
+ {"AB04", 0x2f}, /* xkb:AB04 (AB04) -> linux:47 (KEY_V) -> qnum:47 */
+ {"AB05", 0x30}, /* xkb:AB05 (AB05) -> linux:48 (KEY_B) -> qnum:48 */
+ {"AB06", 0x31}, /* xkb:AB06 (AB06) -> linux:49 (KEY_N) -> qnum:49 */
+ {"AB07", 0x32}, /* xkb:AB07 (AB07) -> linux:50 (KEY_M) -> qnum:50 */
+ {"AB08", 0x33}, /* xkb:AB08 (AB08) -> linux:51 (KEY_COMMA) -> qnum:51 */
+ {"AB09", 0x34}, /* xkb:AB09 (AB09) -> linux:52 (KEY_DOT) -> qnum:52 */
+ {"AB10", 0x35}, /* xkb:AB10 (AB10) -> linux:53 (KEY_SLASH) -> qnum:53 */
+ {"AB11", 0x73}, /* xkb:AB11 (AB11) -> linux:89 (KEY_RO) -> qnum:115 */
+ {"AC01", 0x1e}, /* xkb:AC01 (AC01) -> linux:30 (KEY_A) -> qnum:30 */
+ {"AC02", 0x1f}, /* xkb:AC02 (AC02) -> linux:31 (KEY_S) -> qnum:31 */
+ {"AC03", 0x20}, /* xkb:AC03 (AC03) -> linux:32 (KEY_D) -> qnum:32 */
+ {"AC04", 0x21}, /* xkb:AC04 (AC04) -> linux:33 (KEY_F) -> qnum:33 */
+ {"AC05", 0x22}, /* xkb:AC05 (AC05) -> linux:34 (KEY_G) -> qnum:34 */
+ {"AC06", 0x23}, /* xkb:AC06 (AC06) -> linux:35 (KEY_H) -> qnum:35 */
+ {"AC07", 0x24}, /* xkb:AC07 (AC07) -> linux:36 (KEY_J) -> qnum:36 */
+ {"AC08", 0x25}, /* xkb:AC08 (AC08) -> linux:37 (KEY_K) -> qnum:37 */
+ {"AC09", 0x26}, /* xkb:AC09 (AC09) -> linux:38 (KEY_L) -> qnum:38 */
+ {"AC10", 0x27}, /* xkb:AC10 (AC10) -> linux:39 (KEY_SEMICOLON) -> qnum:39 */
+ {"AC11", 0x28}, /* xkb:AC11 (AC11) -> linux:40 (KEY_APOSTROPHE) -> qnum:40 */
+ {"AC12", 0x2b}, /* xkb:AC12 (AC12) -> linux:43 (KEY_BACKSLASH) -> qnum:43 */
+ {"AD01", 0x10}, /* xkb:AD01 (AD01) -> linux:16 (KEY_Q) -> qnum:16 */
+ {"AD02", 0x11}, /* xkb:AD02 (AD02) -> linux:17 (KEY_W) -> qnum:17 */
+ {"AD03", 0x12}, /* xkb:AD03 (AD03) -> linux:18 (KEY_E) -> qnum:18 */
+ {"AD04", 0x13}, /* xkb:AD04 (AD04) -> linux:19 (KEY_R) -> qnum:19 */
+ {"AD05", 0x14}, /* xkb:AD05 (AD05) -> linux:20 (KEY_T) -> qnum:20 */
+ {"AD06", 0x15}, /* xkb:AD06 (AD06) -> linux:21 (KEY_Y) -> qnum:21 */
+ {"AD07", 0x16}, /* xkb:AD07 (AD07) -> linux:22 (KEY_U) -> qnum:22 */
+ {"AD08", 0x17}, /* xkb:AD08 (AD08) -> linux:23 (KEY_I) -> qnum:23 */
+ {"AD09", 0x18}, /* xkb:AD09 (AD09) -> linux:24 (KEY_O) -> qnum:24 */
+ {"AD10", 0x19}, /* xkb:AD10 (AD10) -> linux:25 (KEY_P) -> qnum:25 */
+ {"AD11", 0x1a}, /* xkb:AD11 (AD11) -> linux:26 (KEY_LEFTBRACE) -> qnum:26 */
+ {"AD12", 0x1b}, /* xkb:AD12 (AD12) -> linux:27 (KEY_RIGHTBRACE) -> qnum:27 */
+ {"AE01", 0x2}, /* xkb:AE01 (AE01) -> linux:2 (KEY_1) -> qnum:2 */
+ {"AE02", 0x3}, /* xkb:AE02 (AE02) -> linux:3 (KEY_2) -> qnum:3 */
+ {"AE03", 0x4}, /* xkb:AE03 (AE03) -> linux:4 (KEY_3) -> qnum:4 */
+ {"AE04", 0x5}, /* xkb:AE04 (AE04) -> linux:5 (KEY_4) -> qnum:5 */
+ {"AE05", 0x6}, /* xkb:AE05 (AE05) -> linux:6 (KEY_5) -> qnum:6 */
+ {"AE06", 0x7}, /* xkb:AE06 (AE06) -> linux:7 (KEY_6) -> qnum:7 */
+ {"AE07", 0x8}, /* xkb:AE07 (AE07) -> linux:8 (KEY_7) -> qnum:8 */
+ {"AE08", 0x9}, /* xkb:AE08 (AE08) -> linux:9 (KEY_8) -> qnum:9 */
+ {"AE09", 0xa}, /* xkb:AE09 (AE09) -> linux:10 (KEY_9) -> qnum:10 */
+ {"AE10", 0xb}, /* xkb:AE10 (AE10) -> linux:11 (KEY_0) -> qnum:11 */
+ {"AE11", 0xc}, /* xkb:AE11 (AE11) -> linux:12 (KEY_MINUS) -> qnum:12 */
+ {"AE12", 0xd}, /* xkb:AE12 (AE12) -> linux:13 (KEY_EQUAL) -> qnum:13 */
+ {"AE13", 0x7d}, /* xkb:AE13 (AE13) -> linux:124 (KEY_YEN) -> qnum:125 */
+ {"AGAI", 0x85}, /* xkb:AGAI (AGAI) -> linux:129 (KEY_AGAIN) -> qnum:133 */
+ {"ALGR", 0xb8}, /* xkb:ALGR (RALT) -> linux:100 (KEY_RIGHTALT) -> qnum:184 */
+ {"BKSL", 0x2b}, /* xkb:BKSL (AC12) -> linux:43 (KEY_BACKSLASH) -> qnum:43 */
+ {"BKSP", 0xe}, /* xkb:BKSP (BKSP) -> linux:14 (KEY_BACKSPACE) -> qnum:14 */
+ {"CAPS", 0x3a}, /* xkb:CAPS (CAPS) -> linux:58 (KEY_CAPSLOCK) -> qnum:58 */
+ {"COMP", 0xdd}, /* xkb:COMP (COMP) -> linux:127 (KEY_COMPOSE) -> qnum:221 */
+ {"COPY", 0xf8}, /* xkb:COPY (COPY) -> linux:133 (KEY_COPY) -> qnum:248 */
+ {"CUT", 0xbc}, /* xkb:CUT (CUT) -> linux:137 (KEY_CUT) -> qnum:188 */
+ {"DEL", 0xd3}, /* xkb:DEL (DELE) -> linux:111 (KEY_DELETE) -> qnum:211 */
+ {"DELE", 0xd3}, /* xkb:DELE (DELE) -> linux:111 (KEY_DELETE) -> qnum:211 */
+ {"DOWN", 0xd0}, /* xkb:DOWN (DOWN) -> linux:108 (KEY_DOWN) -> qnum:208 */
+ {"END", 0xcf}, /* xkb:END (END) -> linux:107 (KEY_END) -> qnum:207 */
+ {"ESC", 0x1}, /* xkb:ESC (ESC) -> linux:1 (KEY_ESC) -> qnum:1 */
+ {"FIND", 0xc1}, /* xkb:FIND (FIND) -> linux:136 (KEY_FIND) -> qnum:193 */
+ {"FK01", 0x3b}, /* xkb:FK01 (FK01) -> linux:59 (KEY_F1) -> qnum:59 */
+ {"FK02", 0x3c}, /* xkb:FK02 (FK02) -> linux:60 (KEY_F2) -> qnum:60 */
+ {"FK03", 0x3d}, /* xkb:FK03 (FK03) -> linux:61 (KEY_F3) -> qnum:61 */
+ {"FK04", 0x3e}, /* xkb:FK04 (FK04) -> linux:62 (KEY_F4) -> qnum:62 */
+ {"FK05", 0x3f}, /* xkb:FK05 (FK05) -> linux:63 (KEY_F5) -> qnum:63 */
+ {"FK06", 0x40}, /* xkb:FK06 (FK06) -> linux:64 (KEY_F6) -> qnum:64 */
+ {"FK07", 0x41}, /* xkb:FK07 (FK07) -> linux:65 (KEY_F7) -> qnum:65 */
+ {"FK08", 0x42}, /* xkb:FK08 (FK08) -> linux:66 (KEY_F8) -> qnum:66 */
+ {"FK09", 0x43}, /* xkb:FK09 (FK09) -> linux:67 (KEY_F9) -> qnum:67 */
+ {"FK10", 0x44}, /* xkb:FK10 (FK10) -> linux:68 (KEY_F10) -> qnum:68 */
+ {"FK11", 0x57}, /* xkb:FK11 (FK11) -> linux:87 (KEY_F11) -> qnum:87 */
+ {"FK12", 0x58}, /* xkb:FK12 (FK12) -> linux:88 (KEY_F12) -> qnum:88 */
+ {"FK13", 0x5d}, /* xkb:FK13 (FK13) -> linux:183 (KEY_F13) -> qnum:93 */
+ {"FK14", 0x5e}, /* xkb:FK14 (FK14) -> linux:184 (KEY_F14) -> qnum:94 */
+ {"FK15", 0x5f}, /* xkb:FK15 (FK15) -> linux:185 (KEY_F15) -> qnum:95 */
+ {"FK16", 0x55}, /* xkb:FK16 (FK16) -> linux:186 (KEY_F16) -> qnum:85 */
+ {"FK17", 0x83}, /* xkb:FK17 (FK17) -> linux:187 (KEY_F17) -> qnum:131 */
+ {"FK18", 0xf7}, /* xkb:FK18 (FK18) -> linux:188 (KEY_F18) -> qnum:247 */
+ {"FK19", 0x84}, /* xkb:FK19 (FK19) -> linux:189 (KEY_F19) -> qnum:132 */
+ {"FK20", 0x5a}, /* xkb:FK20 (FK20) -> linux:190 (KEY_F20) -> qnum:90 */
+ {"FK21", 0x74}, /* xkb:FK21 (FK21) -> linux:191 (KEY_F21) -> qnum:116 */
+ {"FK22", 0xf9}, /* xkb:FK22 (FK22) -> linux:192 (KEY_F22) -> qnum:249 */
+ {"FK23", 0x6d}, /* xkb:FK23 (FK23) -> linux:193 (KEY_F23) -> qnum:109 */
+ {"FK24", 0x6f}, /* xkb:FK24 (FK24) -> linux:194 (KEY_F24) -> qnum:111 */
+ {"FRNT", 0x8c}, /* xkb:FRNT (FRNT) -> linux:132 (KEY_FRONT) -> qnum:140 */
+ {"HELP", 0xf5}, /* xkb:HELP (HELP) -> linux:138 (KEY_HELP) -> qnum:245 */
+ {"HENK", 0x79}, /* xkb:HENK (HENK) -> linux:92 (KEY_HENKAN) -> qnum:121 */
+ {"HIRA", 0x77}, /* xkb:HIRA (HIRA) -> linux:91 (KEY_HIRAGANA) -> qnum:119 */
+ {"HJCV", 0x8d}, /* xkb:HJCV (HJCV) -> linux:123 (KEY_HANJA) -> qnum:141 */
+ {"HKTG", 0x70}, /* xkb:HKTG (HKTG) -> linux:93 (KEY_KATAKANAHIRAGANA) -> qnum:112 */
+ {"HOME", 0xc7}, /* xkb:HOME (HOME) -> linux:102 (KEY_HOME) -> qnum:199 */
+ {"HZTG", 0x76}, /* xkb:HZTG (HZTG) -> linux:85 (KEY_ZENKAKUHANKAKU) -> qnum:118 */
+ {"I120", 0xef}, /* xkb:I120 (I120) -> linux:112 (KEY_MACRO) -> qnum:239 */
+ {"I126", 0xce}, /* xkb:I126 (I126) -> linux:118 (KEY_KPPLUSMINUS) -> qnum:206 */
+ {"I128", 0x8b}, /* xkb:I128 (I128) -> linux:120 (KEY_SCALE) -> qnum:139 */
+ {"I129", 0x7e}, /* xkb:I129 (I129) -> linux:121 (KEY_KPCOMMA) -> qnum:126 */
+ {"I147", 0x9e}, /* xkb:I147 (I147) -> linux:139 (KEY_MENU) -> qnum:158 */
+ {"I148", 0xa1}, /* xkb:I148 (I148) -> linux:140 (KEY_CALC) -> qnum:161 */
+ {"I149", 0x66}, /* xkb:I149 (I149) -> linux:141 (KEY_SETUP) -> qnum:102 */
+ {"I150", 0xdf}, /* xkb:I150 (I150) -> linux:142 (KEY_SLEEP) -> qnum:223 */
+ {"I151", 0xe3}, /* xkb:I151 (I151) -> linux:143 (KEY_WAKEUP) -> qnum:227 */
+ {"I152", 0x67}, /* xkb:I152 (I152) -> linux:144 (KEY_FILE) -> qnum:103 */
+ {"I153", 0x68}, /* xkb:I153 (I153) -> linux:145 (KEY_SENDFILE) -> qnum:104 */
+ {"I154", 0x69}, /* xkb:I154 (I154) -> linux:146 (KEY_DELETEFILE) -> qnum:105 */
+ {"I155", 0x93}, /* xkb:I155 (I155) -> linux:147 (KEY_XFER) -> qnum:147 */
+ {"I156", 0x9f}, /* xkb:I156 (I156) -> linux:148 (KEY_PROG1) -> qnum:159 */
+ {"I157", 0x97}, /* xkb:I157 (I157) -> linux:149 (KEY_PROG2) -> qnum:151 */
+ {"I158", 0x82}, /* xkb:I158 (I158) -> linux:150 (KEY_WWW) -> qnum:130 */
+ {"I159", 0x6a}, /* xkb:I159 (I159) -> linux:151 (KEY_MSDOS) -> qnum:106 */
+ {"I160", 0x92}, /* xkb:I160 (I160) -> linux:152 (KEY_SCREENLOCK) -> qnum:146 */
+ {"I161", 0x6b}, /* xkb:I161 (I161) -> linux:153 (KEY_DIRECTION) -> qnum:107 */
+ {"I162", 0xa6}, /* xkb:I162 (I162) -> linux:154 (KEY_CYCLEWINDOWS) -> qnum:166 */
+ {"I163", 0xec}, /* xkb:I163 (I163) -> linux:155 (KEY_MAIL) -> qnum:236 */
+ {"I164", 0xe6}, /* xkb:I164 (I164) -> linux:156 (KEY_BOOKMARKS) -> qnum:230 */
+ {"I165", 0xeb}, /* xkb:I165 (I165) -> linux:157 (KEY_COMPUTER) -> qnum:235 */
+ {"I166", 0xea}, /* xkb:I166 (I166) -> linux:158 (KEY_BACK) -> qnum:234 */
+ {"I167", 0xe9}, /* xkb:I167 (I167) -> linux:159 (KEY_FORWARD) -> qnum:233 */
+ {"I168", 0xa3}, /* xkb:I168 (I168) -> linux:160 (KEY_CLOSECD) -> qnum:163 */
+ {"I169", 0x6c}, /* xkb:I169 (I169) -> linux:161 (KEY_EJECTCD) -> qnum:108 */
+ {"I170", 0xfd}, /* xkb:I170 (I170) -> linux:162 (KEY_EJECTCLOSECD) -> qnum:253 */
+ {"I171", 0x99}, /* xkb:I171 (I171) -> linux:163 (KEY_NEXTSONG) -> qnum:153 */
+ {"I172", 0xa2}, /* xkb:I172 (I172) -> linux:164 (KEY_PLAYPAUSE) -> qnum:162 */
+ {"I173", 0x90}, /* xkb:I173 (I173) -> linux:165 (KEY_PREVIOUSSONG) -> qnum:144 */
+ {"I174", 0xa4}, /* xkb:I174 (I174) -> linux:166 (KEY_STOPCD) -> qnum:164 */
+ {"I175", 0xb1}, /* xkb:I175 (I175) -> linux:167 (KEY_RECORD) -> qnum:177 */
+ {"I176", 0x98}, /* xkb:I176 (I176) -> linux:168 (KEY_REWIND) -> qnum:152 */
+ {"I177", 0x63}, /* xkb:I177 (I177) -> linux:169 (KEY_PHONE) -> qnum:99 */
+ {"I178", 0x70}, /* xkb:I178 (I178) -> linux:170 (KEY_ISO) -> qnum:112 */
+ {"I179", 0x81}, /* xkb:I179 (I179) -> linux:171 (KEY_CONFIG) -> qnum:129 */
+ {"I180", 0xb2}, /* xkb:I180 (I180) -> linux:172 (KEY_HOMEPAGE) -> qnum:178 */
+ {"I181", 0xe7}, /* xkb:I181 (I181) -> linux:173 (KEY_REFRESH) -> qnum:231 */
+ {"I182", 0x71}, /* xkb:I182 (I182) -> linux:174 (KEY_EXIT) -> qnum:113 */
+ {"I183", 0x72}, /* xkb:I183 (I183) -> linux:175 (KEY_MOVE) -> qnum:114 */
+ {"I184", 0x88}, /* xkb:I184 (I184) -> linux:176 (KEY_EDIT) -> qnum:136 */
+ {"I185", 0x75}, /* xkb:I185 (I185) -> linux:177 (KEY_SCROLLUP) -> qnum:117 */
+ {"I186", 0x8f}, /* xkb:I186 (I186) -> linux:178 (KEY_SCROLLDOWN) -> qnum:143 */
+ {"I187", 0xf6}, /* xkb:I187 (I187) -> linux:179 (KEY_KPLEFTPAREN) -> qnum:246 */
+ {"I188", 0xfb}, /* xkb:I188 (I188) -> linux:180 (KEY_KPRIGHTPAREN) -> qnum:251 */
+ {"I189", 0x89}, /* xkb:I189 (I189) -> linux:181 (KEY_NEW) -> qnum:137 */
+ {"I190", 0x8a}, /* xkb:I190 (I190) -> linux:182 (KEY_REDO) -> qnum:138 */
+ {"I208", 0xa8}, /* xkb:I208 (I208) -> linux:200 (KEY_PLAYCD) -> qnum:168 */
+ {"I209", 0xa9}, /* xkb:I209 (I209) -> linux:201 (KEY_PAUSECD) -> qnum:169 */
+ {"I210", 0xab}, /* xkb:I210 (I210) -> linux:202 (KEY_PROG3) -> qnum:171 */
+ {"I211", 0xac}, /* xkb:I211 (I211) -> linux:203 (KEY_PROG4) -> qnum:172 */
+ {"I212", 0xad}, /* xkb:I212 (I212) -> linux:204 (KEY_DASHBOARD) -> qnum:173 */
+ {"I213", 0xa5}, /* xkb:I213 (I213) -> linux:205 (KEY_SUSPEND) -> qnum:165 */
+ {"I214", 0xaf}, /* xkb:I214 (I214) -> linux:206 (KEY_CLOSE) -> qnum:175 */
+ {"I215", 0xb3}, /* xkb:I215 (I215) -> linux:207 (KEY_PLAY) -> qnum:179 */
+ {"I216", 0xb4}, /* xkb:I216 (I216) -> linux:208 (KEY_FASTFORWARD) -> qnum:180 */
+ {"I217", 0xb6}, /* xkb:I217 (I217) -> linux:209 (KEY_BASSBOOST) -> qnum:182 */
+ {"I218", 0xb9}, /* xkb:I218 (I218) -> linux:210 (KEY_PRINT) -> qnum:185 */
+ {"I219", 0xba}, /* xkb:I219 (I219) -> linux:211 (KEY_HP) -> qnum:186 */
+ {"I220", 0xbb}, /* xkb:I220 (I220) -> linux:212 (KEY_CAMERA) -> qnum:187 */
+ {"I221", 0xbd}, /* xkb:I221 (I221) -> linux:213 (KEY_SOUND) -> qnum:189 */
+ {"I222", 0xbe}, /* xkb:I222 (I222) -> linux:214 (KEY_QUESTION) -> qnum:190 */
+ {"I223", 0xbf}, /* xkb:I223 (I223) -> linux:215 (KEY_EMAIL) -> qnum:191 */
+ {"I224", 0xc0}, /* xkb:I224 (I224) -> linux:216 (KEY_CHAT) -> qnum:192 */
+ {"I225", 0xe5}, /* xkb:I225 (I225) -> linux:217 (KEY_SEARCH) -> qnum:229 */
+ {"I226", 0xc2}, /* xkb:I226 (I226) -> linux:218 (KEY_CONNECT) -> qnum:194 */
+ {"I227", 0xc3}, /* xkb:I227 (I227) -> linux:219 (KEY_FINANCE) -> qnum:195 */
+ {"I228", 0xc4}, /* xkb:I228 (I228) -> linux:220 (KEY_SPORT) -> qnum:196 */
+ {"I229", 0xc5}, /* xkb:I229 (I229) -> linux:221 (KEY_SHOP) -> qnum:197 */
+ {"I230", 0x94}, /* xkb:I230 (I230) -> linux:222 (KEY_ALTERASE) -> qnum:148 */
+ {"I231", 0xca}, /* xkb:I231 (I231) -> linux:223 (KEY_CANCEL) -> qnum:202 */
+ {"I232", 0xcc}, /* xkb:I232 (I232) -> linux:224 (KEY_BRIGHTNESSDOWN) -> qnum:204 */
+ {"I233", 0xd4}, /* xkb:I233 (I233) -> linux:225 (KEY_BRIGHTNESSUP) -> qnum:212 */
+ {"I234", 0xed}, /* xkb:I234 (I234) -> linux:226 (KEY_MEDIA) -> qnum:237 */
+ {"I235", 0xd6}, /* xkb:I235 (I235) -> linux:227 (KEY_SWITCHVIDEOMODE) -> qnum:214 */
+ {"I236", 0xd7}, /* xkb:I236 (I236) -> linux:228 (KEY_KBDILLUMTOGGLE) -> qnum:215 */
+ {"I237", 0xd8}, /* xkb:I237 (I237) -> linux:229 (KEY_KBDILLUMDOWN) -> qnum:216 */
+ {"I238", 0xd9}, /* xkb:I238 (I238) -> linux:230 (KEY_KBDILLUMUP) -> qnum:217 */
+ {"I239", 0xda}, /* xkb:I239 (I239) -> linux:231 (KEY_SEND) -> qnum:218 */
+ {"I240", 0xe4}, /* xkb:I240 (I240) -> linux:232 (KEY_REPLY) -> qnum:228 */
+ {"I241", 0x8e}, /* xkb:I241 (I241) -> linux:233 (KEY_FORWARDMAIL) -> qnum:142 */
+ {"I242", 0xd5}, /* xkb:I242 (I242) -> linux:234 (KEY_SAVE) -> qnum:213 */
+ {"I243", 0xf0}, /* xkb:I243 (I243) -> linux:235 (KEY_DOCUMENTS) -> qnum:240 */
+ {"I244", 0xf1}, /* xkb:I244 (I244) -> linux:236 (KEY_BATTERY) -> qnum:241 */
+ {"I245", 0xf2}, /* xkb:I245 (I245) -> linux:237 (KEY_BLUETOOTH) -> qnum:242 */
+ {"I246", 0xf3}, /* xkb:I246 (I246) -> linux:238 (KEY_WLAN) -> qnum:243 */
+ {"I247", 0xf4}, /* xkb:I247 (I247) -> linux:239 (KEY_UWB) -> qnum:244 */
+ {"INS", 0xd2}, /* xkb:INS (INS) -> linux:110 (KEY_INSERT) -> qnum:210 */
+ {"JPCM", 0x5c}, /* xkb:JPCM (JPCM) -> linux:95 (KEY_KPJPCOMMA) -> qnum:92 */
+ {"KATA", 0x78}, /* xkb:KATA (KATA) -> linux:90 (KEY_KATAKANA) -> qnum:120 */
+ {"KP0", 0x52}, /* xkb:KP0 (KP0) -> linux:82 (KEY_KP0) -> qnum:82 */
+ {"KP1", 0x4f}, /* xkb:KP1 (KP1) -> linux:79 (KEY_KP1) -> qnum:79 */
+ {"KP2", 0x50}, /* xkb:KP2 (KP2) -> linux:80 (KEY_KP2) -> qnum:80 */
+ {"KP3", 0x51}, /* xkb:KP3 (KP3) -> linux:81 (KEY_KP3) -> qnum:81 */
+ {"KP4", 0x4b}, /* xkb:KP4 (KP4) -> linux:75 (KEY_KP4) -> qnum:75 */
+ {"KP5", 0x4c}, /* xkb:KP5 (KP5) -> linux:76 (KEY_KP5) -> qnum:76 */
+ {"KP6", 0x4d}, /* xkb:KP6 (KP6) -> linux:77 (KEY_KP6) -> qnum:77 */
+ {"KP7", 0x47}, /* xkb:KP7 (KP7) -> linux:71 (KEY_KP7) -> qnum:71 */
+ {"KP8", 0x48}, /* xkb:KP8 (KP8) -> linux:72 (KEY_KP8) -> qnum:72 */
+ {"KP9", 0x49}, /* xkb:KP9 (KP9) -> linux:73 (KEY_KP9) -> qnum:73 */
+ {"KPAD", 0x4e}, /* xkb:KPAD (KPAD) -> linux:78 (KEY_KPPLUS) -> qnum:78 */
+ {"KPCO", 0x7e}, /* xkb:KPCO (I129) -> linux:121 (KEY_KPCOMMA) -> qnum:126 */
+ {"KPDC", 0x53}, /* xkb:KPDC (KPDC) -> linux:83 (KEY_KPDOT) -> qnum:83 */
+ {"KPDL", 0x53}, /* xkb:KPDL (KPDC) -> linux:83 (KEY_KPDOT) -> qnum:83 */
+ {"KPDV", 0xb5}, /* xkb:KPDV (KPDV) -> linux:98 (KEY_KPSLASH) -> qnum:181 */
+ {"KPEN", 0x9c}, /* xkb:KPEN (KPEN) -> linux:96 (KEY_KPENTER) -> qnum:156 */
+ {"KPEQ", 0x59}, /* xkb:KPEQ (KPEQ) -> linux:117 (KEY_KPEQUAL) -> qnum:89 */
+ {"KPMU", 0x37}, /* xkb:KPMU (KPMU) -> linux:55 (KEY_KPASTERISK) -> qnum:55 */
+ {"KPSP", 0x5c}, /* xkb:KPSP (JPCM) -> linux:95 (KEY_KPJPCOMMA) -> qnum:92 */
+ {"KPSU", 0x4a}, /* xkb:KPSU (KPSU) -> linux:74 (KEY_KPMINUS) -> qnum:74 */
+ {"LALT", 0x38}, /* xkb:LALT (LALT) -> linux:56 (KEY_LEFTALT) -> qnum:56 */
+ {"LCTL", 0x1d}, /* xkb:LCTL (LCTL) -> linux:29 (KEY_LEFTCTRL) -> qnum:29 */
+ {"LEFT", 0xcb}, /* xkb:LEFT (LEFT) -> linux:105 (KEY_LEFT) -> qnum:203 */
+ {"LFSH", 0x2a}, /* xkb:LFSH (LFSH) -> linux:42 (KEY_LEFTSHIFT) -> qnum:42 */
+ {"LMTA", 0xdb}, /* xkb:LMTA (LWIN) -> linux:125 (KEY_LEFTMETA) -> qnum:219 */
+ {"LNFD", 0x5b}, /* xkb:LNFD (LNFD) -> linux:101 (KEY_LINEFEED) -> qnum:91 */
+ {"LSGT", 0x56}, /* xkb:LSGT (LSGT) -> linux:86 (KEY_102ND) -> qnum:86 */
+ {"LWIN", 0xdb}, /* xkb:LWIN (LWIN) -> linux:125 (KEY_LEFTMETA) -> qnum:219 */
+ {"MENU", 0xdd}, /* xkb:MENU (COMP) -> linux:127 (KEY_COMPOSE) -> qnum:221 */
+ {"MUHE", 0x7b}, /* xkb:MUHE (MUHE) -> linux:94 (KEY_MUHENKAN) -> qnum:123 */
+ {"MUTE", 0xa0}, /* xkb:MUTE (MUTE) -> linux:113 (KEY_MUTE) -> qnum:160 */
+ {"NFER", 0x7b}, /* xkb:NFER (MUHE) -> linux:94 (KEY_MUHENKAN) -> qnum:123 */
+ {"NMLK", 0x45}, /* xkb:NMLK (NMLK) -> linux:69 (KEY_NUMLOCK) -> qnum:69 */
+ {"OPEN", 0x64}, /* xkb:OPEN (OPEN) -> linux:134 (KEY_OPEN) -> qnum:100 */
+ {"PAST", 0x65}, /* xkb:PAST (PAST) -> linux:135 (KEY_PASTE) -> qnum:101 */
+ {"PAUS", 0xc6}, /* xkb:PAUS (PAUS) -> linux:119 (KEY_PAUSE) -> qnum:198 */
+ {"PGDN", 0xd1}, /* xkb:PGDN (PGDN) -> linux:109 (KEY_PAGEDOWN) -> qnum:209 */
+ {"PGUP", 0xc9}, /* xkb:PGUP (PGUP) -> linux:104 (KEY_PAGEUP) -> qnum:201 */
+ {"POWR", 0xde}, /* xkb:POWR (POWR) -> linux:116 (KEY_POWER) -> qnum:222 */
+ {"PROP", 0x86}, /* xkb:PROP (PROP) -> linux:130 (KEY_PROPS) -> qnum:134 */
+ {"PRSC", 0x54}, /* xkb:PRSC (SYRQ) -> linux:99 (KEY_SYSRQ) -> qnum:84 */
+ {"RALT", 0xb8}, /* xkb:RALT (RALT) -> linux:100 (KEY_RIGHTALT) -> qnum:184 */
+ {"RCTL", 0x9d}, /* xkb:RCTL (RCTL) -> linux:97 (KEY_RIGHTCTRL) -> qnum:157 */
+ {"RGHT", 0xcd}, /* xkb:RGHT (RGHT) -> linux:106 (KEY_RIGHT) -> qnum:205 */
+ {"RMTA", 0xdc}, /* xkb:RMTA (RWIN) -> linux:126 (KEY_RIGHTMETA) -> qnum:220 */
+ {"RTRN", 0x1c}, /* xkb:RTRN (RTRN) -> linux:28 (KEY_ENTER) -> qnum:28 */
+ {"RTSH", 0x36}, /* xkb:RTSH (RTSH) -> linux:54 (KEY_RIGHTSHIFT) -> qnum:54 */
+ {"RWIN", 0xdc}, /* xkb:RWIN (RWIN) -> linux:126 (KEY_RIGHTMETA) -> qnum:220 */
+ {"SCLK", 0x46}, /* xkb:SCLK (SCLK) -> linux:70 (KEY_SCROLLLOCK) -> qnum:70 */
+ {"SPCE", 0x39}, /* xkb:SPCE (SPCE) -> linux:57 (KEY_SPACE) -> qnum:57 */
+ {"STOP", 0xe8}, /* xkb:STOP (STOP) -> linux:128 (KEY_STOP) -> qnum:232 */
+ {"SYRQ", 0x54}, /* xkb:SYRQ (SYRQ) -> linux:99 (KEY_SYSRQ) -> qnum:84 */
+ {"TAB", 0xf}, /* xkb:TAB (TAB) -> linux:15 (KEY_TAB) -> qnum:15 */
+ {"TLDE", 0x29}, /* xkb:TLDE (AB00) -> linux:41 (KEY_GRAVE) -> qnum:41 */
+ {"UNDO", 0x87}, /* xkb:UNDO (UNDO) -> linux:131 (KEY_UNDO) -> qnum:135 */
+ {"UP", 0xc8}, /* xkb:UP (UP) -> linux:103 (KEY_UP) -> qnum:200 */
+ {"VOL+", 0xb0}, /* xkb:VOL+ (VOL+) -> linux:115 (KEY_VOLUMEUP) -> qnum:176 */
+ {"VOL-", 0xae}, /* xkb:VOL- (VOL-) -> linux:114 (KEY_VOLUMEDOWN) -> qnum:174 */
+ {"XFER", 0x93}, /* xkb:XFER (I155) -> linux:147 (KEY_XFER) -> qnum:147 */
+};
+const unsigned int code_map_xkb_to_qnum_len = sizeof(code_map_xkb_to_qnum)/sizeof(code_map_xkb_to_qnum[0]);
diff --git a/win/rfb_win32/SDisplay.cxx b/win/rfb_win32/SDisplay.cxx
index ac64e3e..ad55d49 100644
--- a/win/rfb_win32/SDisplay.cxx
+++ b/win/rfb_win32/SDisplay.cxx
@@ -30,6 +30,7 @@
#include <rfb_win32/SDisplayCoreWMHooks.h>
#include <rfb/Exception.h>
#include <rfb/LogWriter.h>
+#include <rfb/ledStates.h>
using namespace rdr;
@@ -65,7 +66,7 @@
: server(0), pb(0), device(0),
core(0), ptr(0), kbd(0), clipboard(0),
inputs(0), monitor(0), cleanDesktop(0), cursor(0),
- statusLocation(0)
+ statusLocation(0), ledState(0)
{
updateEvent.h = CreateEvent(0, TRUE, FALSE, 0);
}
@@ -196,6 +197,10 @@
cleanDesktop->disableEffects();
isWallpaperRemoved = removeWallpaper;
areEffectsDisabled = disableEffects;
+
+ checkLedState();
+ if (server)
+ server->setLEDState(ledState);
}
void SDisplay::stopCore() {
@@ -275,12 +280,30 @@
}
}
-void SDisplay::keyEvent(rdr::U32 key, bool down) {
+void SDisplay::keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down) {
// - Check that the SDesktop doesn't need restarting
if (isRestartRequired())
restartCore();
if (kbd)
- kbd->keyEvent(key, down);
+ kbd->keyEvent(keysym, keycode, down);
+}
+
+bool SDisplay::checkLedState() {
+ unsigned state = 0;
+
+ if (GetKeyState(VK_SCROLL) & 0x0001)
+ state |= ledScrollLock;
+ if (GetKeyState(VK_NUMLOCK) & 0x0001)
+ state |= ledNumLock;
+ if (GetKeyState(VK_CAPITAL) & 0x0001)
+ state |= ledCapsLock;
+
+ if (ledState != state) {
+ ledState = state;
+ return true;
+ }
+
+ return false;
}
void SDisplay::clientCutText(const char* text, int len) {
@@ -384,6 +407,10 @@
// Flush any changes to the server
flushChangeTracker();
+
+ // Forward current LED state to the server
+ if (checkLedState())
+ server->setLEDState(ledState);
}
return;
}
diff --git a/win/rfb_win32/SDisplay.h b/win/rfb_win32/SDisplay.h
index b021782..9892ed9 100644
--- a/win/rfb_win32/SDisplay.h
+++ b/win/rfb_win32/SDisplay.h
@@ -66,7 +66,7 @@
virtual void start(VNCServer* vs);
virtual void stop();
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 Point getFbSize();
@@ -106,6 +106,7 @@
void restartCore();
void recreatePixelBuffer(bool force=false);
bool flushChangeTracker(); // true if flushed, false if empty
+ bool checkLedState();
VNCServer* server;
@@ -151,6 +152,8 @@
// -=- Where to write the active/inactive indicator to
bool* statusLocation;
+
+ unsigned ledState;
};
}
diff --git a/win/rfb_win32/SInput.cxx b/win/rfb_win32/SInput.cxx
index 0923118..f6075c1 100644
--- a/win/rfb_win32/SInput.cxx
+++ b/win/rfb_win32/SInput.cxx
@@ -134,6 +134,9 @@
BoolParameter rfb::win32::SKeyboard::deadKeyAware("DeadKeyAware",
"Whether to assume the viewer has already interpreted dead key sequences "
"into latin-1 characters", true);
+BoolParameter rfb::win32::SKeyboard::rawKeyboard("RawKeyboard",
+ "Send keyboard events straight through and avoid mapping them to the "
+ "current keyboard layout", false);
// The keysymToAscii table transforms a couple of awkward keysyms into their
// ASCII equivalents.
@@ -229,6 +232,31 @@
keybd_event(vkCode, MapVirtualKey(vkCode, 0), flags, 0);
}
+inline void doScanCodeEvent(BYTE scancode, bool down) {
+ INPUT evt;
+
+ evt.type = INPUT_KEYBOARD;
+ evt.ki.wVk = 0;
+ evt.ki.dwFlags = KEYEVENTF_SCANCODE;
+
+ if (!down)
+ evt.ki.dwFlags |= KEYEVENTF_KEYUP;
+
+ if (scancode & 0x80) {
+ evt.ki.dwFlags |= KEYEVENTF_EXTENDEDKEY;
+ scancode &= ~0x80;
+ }
+
+ evt.ki.wScan = scancode;
+ evt.ki.dwExtraInfo = 0;
+ evt.ki.time = 0;
+ vlog.debug("SendInput ScanCode: 0x%x Flags: 0x%lx %s", scancode,
+ evt.ki.dwFlags, down ? "Down" : "Up");
+
+ if (SendInput(1, &evt, sizeof(evt)) != 1)
+ vlog.error("SendInput %lu", GetLastError());
+}
+
// KeyStateModifier is a class which helps simplify generating a "fake" press
// or release of shift, ctrl, alt, etc. An instance of the class is created
// for every key which may need to be pressed or released. Then either press()
@@ -321,8 +349,15 @@
}
-void win32::SKeyboard::keyEvent(rdr::U32 keysym, bool down)
+void win32::SKeyboard::keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down)
{
+ // If scan code is available use that directly as windows uses
+ // compatible scancodes
+ if (keycode && rawKeyboard) {
+ doScanCodeEvent(keycode, down);
+ return;
+ }
+
for (unsigned int i = 0; i < sizeof(keysymToAscii) / sizeof(keysymToAscii_t); i++) {
if (keysymToAscii[i].keysym == keysym) {
keysym = keysymToAscii[i].ascii;
diff --git a/win/rfb_win32/SInput.h b/win/rfb_win32/SInput.h
index 2a0b3e6..c927c65 100644
--- a/win/rfb_win32/SInput.h
+++ b/win/rfb_win32/SInput.h
@@ -53,8 +53,9 @@
class SKeyboard {
public:
SKeyboard();
- void keyEvent(rdr::U32 key, bool down);
+ void keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down);
static BoolParameter deadKeyAware;
+ static BoolParameter rawKeyboard;
private:
std::map<rdr::U32,rdr::U8> vkMap;
std::map<rdr::U32,bool> extendedMap;
diff --git a/win/vncconfig/Inputs.h b/win/vncconfig/Inputs.h
index 2ad3b42..f290831 100644
--- a/win/vncconfig/Inputs.h
+++ b/win/vncconfig/Inputs.h
@@ -37,6 +37,7 @@
regKey(rk), enableAffectSSaver(true) {}
void initDialog() {
setItemChecked(IDC_ACCEPT_KEYS, rfb::Server::acceptKeyEvents);
+ setItemChecked(IDC_RAW_KEYBOARD, SKeyboard::rawKeyboard);
setItemChecked(IDC_ACCEPT_PTR, rfb::Server::acceptPointerEvents);
setItemChecked(IDC_ACCEPT_CUTTEXT, rfb::Server::acceptCutText);
setItemChecked(IDC_SEND_CUTTEXT, rfb::Server::sendCutText);
@@ -52,6 +53,7 @@
BOOL inputResetsBlocked;
SystemParametersInfo(SPI_GETBLOCKSENDINPUTRESETS, 0, &inputResetsBlocked, 0);
setChanged((rfb::Server::acceptKeyEvents != isItemChecked(IDC_ACCEPT_KEYS)) ||
+ (SKeyboard::rawKeyboard != isItemChecked(IDC_RAW_KEYBOARD)) ||
(rfb::Server::acceptPointerEvents != isItemChecked(IDC_ACCEPT_PTR)) ||
(rfb::Server::acceptCutText != isItemChecked(IDC_ACCEPT_CUTTEXT)) ||
(rfb::Server::sendCutText != isItemChecked(IDC_SEND_CUTTEXT)) ||
@@ -61,6 +63,7 @@
}
bool onOk() {
regKey.setBool(_T("AcceptKeyEvents"), isItemChecked(IDC_ACCEPT_KEYS));
+ regKey.setBool(_T("RawKeyboard"), isItemChecked(IDC_RAW_KEYBOARD));
regKey.setBool(_T("AcceptPointerEvents"), isItemChecked(IDC_ACCEPT_PTR));
regKey.setBool(_T("AcceptCutText"), isItemChecked(IDC_ACCEPT_CUTTEXT));
regKey.setBool(_T("SendCutText"), isItemChecked(IDC_SEND_CUTTEXT));
diff --git a/win/vncconfig/resource.h b/win/vncconfig/resource.h
index 243f824..16bbc15 100644
--- a/win/vncconfig/resource.h
+++ b/win/vncconfig/resource.h
@@ -77,6 +77,7 @@
#define IDC_RFB_ENABLE 1082
#define IDC_LOAD_CERT 1087
#define IDC_LOAD_CERTKEY 1088
+#define IDC_RAW_KEYBOARD 1089
#define ID_OPTIONS 40001
#define ID_CLOSE 40002
#define ID_ABOUT 40003
diff --git a/win/vncconfig/vncconfig.rc b/win/vncconfig/vncconfig.rc
index 4da1a3b..f23bbdb 100644
--- a/win/vncconfig/vncconfig.rc
+++ b/win/vncconfig/vncconfig.rc
@@ -206,17 +206,19 @@
"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,10,172,15
CONTROL "Accept keyboard events from clients",IDC_ACCEPT_KEYS,
"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,25,172,15
+ CONTROL "Send raw keyboard events to applications",IDC_RAW_KEYBOARD,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,40,172,15
CONTROL "Accept clipboard updates from clients",
IDC_ACCEPT_CUTTEXT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
- 7,40,172,15
+ 7,55,172,15
CONTROL "Send clipboard updates to clients",IDC_SEND_CUTTEXT,
- "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,55,172,15
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,70,172,15
CONTROL "Allow input events to affect the screen-saver",
IDC_AFFECT_SCREENSAVER,"Button",BS_AUTOCHECKBOX |
- WS_TABSTOP,7,70,172,15
+ WS_TABSTOP,7,85,172,15
CONTROL "Disable local inputs while server is in use",
IDC_DISABLE_LOCAL_INPUTS,"Button",BS_AUTOCHECKBOX |
- WS_TABSTOP,7,95,172,15
+ WS_TABSTOP,7,110,172,15
END
IDD_ABOUT DIALOG DISCARDABLE 0, 0, 300, 92