Basic support for QEMU Extended Key Events

This adds the basic infrastructure and handshake for the QEMU
Extended Key Events extension. No viewer or server makes use of
the extra functionality yet though.
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);
+}