diff --git a/XenAdmin/ConsoleView/RdpClient.cs b/XenAdmin/ConsoleView/RdpClient.cs index 008d2f4b2..d85948be4 100644 --- a/XenAdmin/ConsoleView/RdpClient.cs +++ b/XenAdmin/ConsoleView/RdpClient.cs @@ -294,7 +294,7 @@ namespace XenAdmin.ConsoleView internal Set pressedScans = new Set(); private bool modifierKeyPressedAlone = false; - private void handleRDPKey(bool pressed, int scancode) + private void handleRDPKey(bool pressed, int scancode, int keysym) { bool containsFocus = parent.ParentForm != null && parent.ParentForm.ContainsFocus; diff --git a/XenAdmin/ConsoleView/VNCGraphicsClient.cs b/XenAdmin/ConsoleView/VNCGraphicsClient.cs index 7d82bb9ca..d932315ae 100644 --- a/XenAdmin/ConsoleView/VNCGraphicsClient.cs +++ b/XenAdmin/ConsoleView/VNCGraphicsClient.cs @@ -964,7 +964,7 @@ namespace XenAdmin.ConsoleView foreach (int key in pressedScans) { - this.vncStream.keyScanEvent(false, key); + this.vncStream.keyScanEvent(false, key, -1, Helpers.InvernessOrGreater(this.SourceVM.Connection)); } }); @@ -1285,7 +1285,7 @@ namespace XenAdmin.ConsoleView } } - public void keyScan(bool pressed, int scanCode) + public void keyScan(bool pressed, int scanCode, int keySym) { if (KeyHandler.handleExtras(pressed, pressedScans, KeyHandler.ExtraScans, scanCode, KeyHandler.ModifierScans, ref modifierKeyPressedAlone)) { @@ -1293,21 +1293,26 @@ namespace XenAdmin.ConsoleView { // send key up anyway modifierKeyPressedAlone = false; - keyScan_(pressed, scanCode); + keyScan_(pressed, scanCode, keySym); return; } this.Focus(); return; } - keyScan_(pressed, scanCode); + keyScan_(pressed, scanCode, keySym); } private void keyScan_(bool pressed, int scanCode) + { + keyScan_(pressed, scanCode, -1); + } + + private void keyScan_(bool pressed, int scanCode, int keySym) { DoIfConnected(delegate() { - this.vncStream.keyScanEvent(pressed, scanCode); + this.vncStream.keyScanEvent(pressed, scanCode, keySym, Helpers.InvernessOrGreater(this.SourceVM.Connection)); }); } diff --git a/XenCenterVNC/KeyMap.cs b/XenCenterVNC/KeyMap.cs index 360ecf190..57521cc4d 100644 --- a/XenCenterVNC/KeyMap.cs +++ b/XenCenterVNC/KeyMap.cs @@ -150,7 +150,7 @@ namespace DotNetVnc private static LowLevelKeyboardProc _proc = HookCallback; private static IntPtr _hookID = IntPtr.Zero; - public delegate void KeyEvent(bool down, int scancode); + public delegate void KeyEvent(bool down, int scancode, int keysym); private static KeyEvent keyEvent = null; #pragma warning disable 0649 @@ -195,7 +195,8 @@ namespace DotNetVnc } } - private const int NUM_LOCK_SCAN = 197; + private const int RIGHT_SHIFT_SCAN = 54; + private const int NUM_LOCK_SCAN = 69; private static int HookCallback(int nCode, int wParam, KBDLLHOOKSTRUCT* lParam) { @@ -207,22 +208,40 @@ namespace DotNetVnc { KBDLLHOOKSTRUCT kbStruct = *lParam; - bool extended = (kbStruct.flags & FLAG_EXTENDED) == 0; + bool extended = (kbStruct.flags & FLAG_EXTENDED) == FLAG_EXTENDED; bool down = (wParam == WM_KEYDOWN) || (wParam == WM_SYSKEYDOWN); int scanCode = kbStruct.scanCode; + int keySym = KeyMap.translateKey((Keys)kbStruct.vkCode); + + /* kbStruct.scanCode for NUM_LOCK and PAUSE are the same (69). + * But NUM_LOCK is an extended key, where as PAUSE is not. + * QEMU doesn't support PAUSE and expects NUM_LOCK scanCode + * to be sent as 69 + */ switch (scanCode) { - case 54: - break; + /* Although RIGHT_SHIFT, NUMS_LOCK are extended keys, + * scan code for these keys are not prefixed with 0xe0. + */ + case RIGHT_SHIFT_SCAN: + case NUM_LOCK_SCAN: + break; default: - scanCode += (extended ? 0 : 128); + /* 128 is added to scanCode to differentiate + * an extended key. Scan code for all extended keys + * needs to be prefixed with 0xe0, so adding 128 + * or ( | 0x80) will give a hint to qemu that this + * scanCode is an extended one and qemu can then prefix + * scanCode with 0xe0 + */ + scanCode += (extended ? 128 : 0); break; } if (InterceptKeys.keyEvent != null) { - InterceptKeys.keyEvent(down, scanCode); + InterceptKeys.keyEvent(down, scanCode, keySym); } if (bubble || scanCode == NUM_LOCK_SCAN) diff --git a/XenCenterVNC/VNCStream.cs b/XenCenterVNC/VNCStream.cs index afa7ca5e5..a2341d272 100644 --- a/XenCenterVNC/VNCStream.cs +++ b/XenCenterVNC/VNCStream.cs @@ -54,12 +54,14 @@ namespace DotNetVnc private const int CURSOR_PSEUDO_ENCODING = -239; private const int DESKTOP_SIZE_PSEUDO_ENCODING = -223; private const int XENCENTER_ENCODING = -254; + private const int QEMU_EXT_KEY_ENCODING = -258; private const int SET_PIXEL_FORMAT = 0; private const int SET_ENCODINGS = 2; private const int FRAMEBUFFER_UPDATE_REQUEST = 3; private const int KEY_EVENT = 4; private const int KEY_SCAN_EVENT = 254; + private const int QEMU_MSG = 255; private const int POINTER_EVENT = 5; private const int CLIENT_CUT_TEXT = 6; @@ -73,6 +75,8 @@ namespace DotNetVnc private const int BELL = 2; private const int SERVER_CUT_TEXT = 3; + private const int QEMU_EXT_KEY_EVENT = 0; + private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); private Thread thread = null; @@ -118,7 +122,8 @@ namespace DotNetVnc RAW_ENCODING, CURSOR_PSEUDO_ENCODING, DESKTOP_SIZE_PSEUDO_ENCODING, - XENCENTER_ENCODING + XENCENTER_ENCODING, + QEMU_EXT_KEY_ENCODING }; private readonly IVNCGraphicsClient client; @@ -134,6 +139,7 @@ namespace DotNetVnc private int height; private bool incremental; + private bool qemu_ext_key_encoding = false; private PixelFormat pixelFormat; private PixelFormat pixelFormatCursor; @@ -468,13 +474,36 @@ namespace DotNetVnc this.stream.writeInt32(key); } - public void keyScanEvent(bool down, int key) + private void writeQemuExtKey(int command, bool down, int key, int sym) + { + this.stream.writeInt8(command); + this.stream.writeInt8(QEMU_EXT_KEY_EVENT); + this.stream.writePadding(1); + this.stream.writeFlag(down); + this.stream.writeInt32(sym); + this.stream.writeInt32(key); + } + + /** + * use_qemu_ext_key_encoding: Dictates if we want to use QEMU_EXT_KEY encoding. + * + * XS6.2 doesn't properly support QEMU_EXT_KEY and XS6.5 supports QEMU_EXT_KEY encoding + * only if XS65ESP1051 is applied, so restrict QEMU_EXT_KEY encoding to Inverness and above. + */ + public void keyScanEvent(bool down, int key, int sym, bool use_qemu_ext_key_encoding) { lock (this.writeLock) { try { - writeKey(KEY_SCAN_EVENT, down, key); + if (qemu_ext_key_encoding && use_qemu_ext_key_encoding) + { + writeQemuExtKey(QEMU_MSG, down, key, sym); + } + else + { + writeKey(KEY_SCAN_EVENT, down, key); + } this.stream.Flush(); } catch (IOException e) @@ -1274,6 +1303,10 @@ namespace DotNetVnc incremental = false; break; + case QEMU_EXT_KEY_ENCODING: + qemu_ext_key_encoding = true; + break; + default: throw new VNCException("unimplemented encoding: " + encoding); }