mirror of
https://github.com/xcp-ng/xenadmin.git
synced 2024-11-25 06:16:37 +01:00
88fa402e21
Since the standalone VNC control was removed, the VNC code has been used only by the UI project. According to Microsoft's performance guidelines large single assemblies are preferable to multiple smaller ones. Signed-off-by: Konstantina Chremmou <konstantina.chremmou@citrix.com>
268 lines
9.1 KiB
C#
268 lines
9.1 KiB
C#
/* Copyright (c) Citrix Systems, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms,
|
|
* with or without modification, are permitted provided
|
|
* that the following conditions are met:
|
|
*
|
|
* * Redistributions of source code must retain the above
|
|
* copyright notice, this list of conditions and the
|
|
* following disclaimer.
|
|
* * Redistributions in binary form must reproduce the above
|
|
* copyright notice, this list of conditions and the
|
|
* following disclaimer in the documentation and/or other
|
|
* materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
|
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
|
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
|
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
|
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*/
|
|
|
|
|
|
using System;
|
|
using System.Windows.Forms;
|
|
using System.Runtime.InteropServices;
|
|
using System.Diagnostics;
|
|
using System.Resources;
|
|
using System.Collections.Generic;
|
|
using System.Globalization;
|
|
using System.Text;
|
|
using XenCenterLib;
|
|
|
|
namespace DotNetVnc
|
|
{
|
|
/// <summary>
|
|
/// Translates C# Keys (i.e. virtual key codes) into X11 keysyms.
|
|
/// </summary>
|
|
public class KeyMap
|
|
{
|
|
private static Dictionary<Keys, int> map = new Dictionary<Keys, int>();
|
|
|
|
static KeyMap()
|
|
{
|
|
ResourceManager resources = new ResourceManager("DotNetVnc.KeyMap", typeof(KeyMap).Assembly);
|
|
foreach (Keys key in Enum.GetValues(typeof(Keys)))
|
|
{
|
|
int sym = parse_keysym(resources.GetString(Enum.GetName(typeof(Keys), key)));
|
|
if (sym != -1)
|
|
map[key] = sym;
|
|
}
|
|
}
|
|
|
|
private static int parse_keysym(string s)
|
|
{
|
|
if (s != null)
|
|
{
|
|
NumberStyles style;
|
|
|
|
if (s.StartsWith("0x") || s.StartsWith("0X"))
|
|
{
|
|
s = s.Substring(2);
|
|
style = NumberStyles.HexNumber;
|
|
}
|
|
else
|
|
{
|
|
style = NumberStyles.Integer;
|
|
}
|
|
|
|
int keysym;
|
|
if (int.TryParse(s, style, null, out keysym))
|
|
{
|
|
return keysym;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
public static int translateKey(Keys key)
|
|
{
|
|
return IsMapped(key) ? map[key] : UnicodeOfKey(key);
|
|
}
|
|
|
|
public static bool IsMapped(Keys key)
|
|
{
|
|
return map.ContainsKey(key);
|
|
}
|
|
|
|
private static IntPtr keyboard_state = Marshal.AllocHGlobal(256);
|
|
private static StringBuilder char_buffer = new StringBuilder(Win32.TO_UNICODE_BUFFER_SIZE);
|
|
|
|
private static int UnicodeOfKey(Keys key)
|
|
{
|
|
try
|
|
{
|
|
Win32.GetKeyboardState(keyboard_state);
|
|
int n = Win32.ToUnicode((uint)key, 0, keyboard_state, char_buffer, Win32.TO_UNICODE_BUFFER_SIZE, 0);
|
|
|
|
if (n == 1)
|
|
{
|
|
int k = char_buffer[0];
|
|
|
|
if (k < 0x20)
|
|
{
|
|
return k + 0x60;
|
|
}
|
|
else
|
|
{
|
|
return k;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return -1;
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
public unsafe class InterceptKeys
|
|
{
|
|
private static bool bubble = false;
|
|
|
|
private const int WH_KEYBOARD_LL = 13;
|
|
private const int WM_KEYDOWN = 0x0100;
|
|
private const int WM_KEYUP = 0x0101;
|
|
private const int WM_SYSKEYDOWN = 0x0104;
|
|
private const int FLAG_EXTENDED = 0x01;
|
|
|
|
private delegate int LowLevelKeyboardProc(
|
|
int nCode, int wParam, KBDLLHOOKSTRUCT* lParam);
|
|
|
|
private static LowLevelKeyboardProc _proc = HookCallback;
|
|
private static IntPtr _hookID = IntPtr.Zero;
|
|
|
|
public delegate void KeyEvent(bool down, int scancode, int keysym);
|
|
private static KeyEvent keyEvent = null;
|
|
|
|
#pragma warning disable 0649
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
private struct KBDLLHOOKSTRUCT
|
|
{
|
|
public int vkCode;
|
|
public int scanCode;
|
|
public int flags;
|
|
public int time;
|
|
public int dwExtraInfo;
|
|
}
|
|
#pragma warning restore 0649
|
|
|
|
public static void grabKeys(KeyEvent keyEvent, bool bubble)
|
|
{
|
|
InterceptKeys.bubble = bubble;
|
|
|
|
if (InterceptKeys.keyEvent == null)
|
|
{
|
|
InterceptKeys.keyEvent = keyEvent;
|
|
_hookID = SetHook(_proc);
|
|
}
|
|
}
|
|
|
|
public static void releaseKeys()
|
|
{
|
|
if (InterceptKeys.keyEvent != null)
|
|
{
|
|
InterceptKeys.keyEvent = null;
|
|
UnhookWindowsHookEx(_hookID);
|
|
}
|
|
}
|
|
|
|
private static IntPtr SetHook(LowLevelKeyboardProc proc)
|
|
{
|
|
using (Process curProcess = Process.GetCurrentProcess())
|
|
using (ProcessModule curModule = curProcess.MainModule)
|
|
{
|
|
return SetWindowsHookEx(WH_KEYBOARD_LL, proc,
|
|
Win32.GetModuleHandle(curModule.ModuleName), 0);
|
|
}
|
|
}
|
|
|
|
private const int RIGHT_SHIFT_SCAN = 54;
|
|
private const int NUM_LOCK_SCAN = 69;
|
|
|
|
private static int HookCallback(int nCode, int wParam, KBDLLHOOKSTRUCT* lParam)
|
|
{
|
|
if (nCode < 0)
|
|
{
|
|
return CallNextHookEx(_hookID, nCode, wParam, lParam);
|
|
}
|
|
else
|
|
{
|
|
KBDLLHOOKSTRUCT kbStruct = *lParam;
|
|
|
|
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)
|
|
{
|
|
/* 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:
|
|
/* 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, keySym);
|
|
}
|
|
|
|
if (bubble || scanCode == NUM_LOCK_SCAN)
|
|
{
|
|
return CallNextHookEx(_hookID, nCode, wParam, lParam);
|
|
}
|
|
else
|
|
{
|
|
return 1; // Prevent the message being passed on.
|
|
}
|
|
}
|
|
}
|
|
|
|
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
|
private static extern IntPtr SetWindowsHookEx(int idHook,
|
|
LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);
|
|
|
|
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
|
[return: MarshalAs(UnmanagedType.Bool)]
|
|
private static extern bool UnhookWindowsHookEx(IntPtr hhk);
|
|
|
|
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
|
private static extern int CallNextHookEx(IntPtr hhk, int nCode,
|
|
int wParam, KBDLLHOOKSTRUCT * lParam);
|
|
|
|
}
|
|
}
|