csharpc-sharpdotnetxamlavaloniauicross-platformcross-platform-xamlavaloniaguimulti-platformuser-interfacedotnetcore
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
532 lines
21 KiB
532 lines
21 KiB
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using Avalonia.Input;
|
|
using static Avalonia.Win32.Interop.UnmanagedMethods;
|
|
using static Avalonia.Win32.Interop.UnmanagedMethods.VirtualKeyStates;
|
|
|
|
namespace Avalonia.Win32.Input
|
|
{
|
|
/// <summary>
|
|
/// Contains methods used to translate a Windows virtual/physical key to an Avalonia <see cref="Key"/>.
|
|
/// </summary>
|
|
public static class KeyInterop
|
|
{
|
|
// source: https://learn.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes
|
|
private static readonly Dictionary<Key, int> s_virtualKeyFromKey = new(169)
|
|
{
|
|
{ Key.Cancel, (int)VK_CANCEL },
|
|
{ Key.Back, (int)VK_BACK },
|
|
{ Key.Tab, (int)VK_TAB },
|
|
{ Key.Clear, (int)VK_CLEAR },
|
|
{ Key.Return, (int)VK_RETURN },
|
|
{ Key.Pause, (int)VK_PAUSE },
|
|
{ Key.Capital, (int)VK_CAPITAL },
|
|
{ Key.KanaMode, (int)VK_KANA },
|
|
{ Key.JunjaMode, (int)VK_JUNJA },
|
|
{ Key.FinalMode, (int)VK_FINAL },
|
|
{ Key.HanjaMode, (int)VK_HANJA },
|
|
{ Key.Escape, (int)VK_ESCAPE },
|
|
{ Key.ImeConvert, (int)VK_CONVERT },
|
|
{ Key.ImeNonConvert, (int)VK_NONCONVERT },
|
|
{ Key.ImeAccept, (int)VK_ACCEPT },
|
|
{ Key.ImeModeChange, (int)VK_MODECHANGE },
|
|
{ Key.Space, (int)VK_SPACE },
|
|
{ Key.PageUp, (int)VK_PRIOR },
|
|
{ Key.PageDown, (int)VK_NEXT },
|
|
{ Key.End, (int)VK_END },
|
|
{ Key.Home, (int)VK_HOME },
|
|
{ Key.Left, (int)VK_LEFT },
|
|
{ Key.Up, (int)VK_UP },
|
|
{ Key.Right, (int)VK_RIGHT },
|
|
{ Key.Down, (int)VK_DOWN },
|
|
{ Key.Select, (int)VK_SELECT },
|
|
{ Key.Print, (int)VK_PRINT },
|
|
{ Key.Execute, (int)VK_EXECUTE },
|
|
{ Key.Snapshot, (int)VK_SNAPSHOT },
|
|
{ Key.Insert, (int)VK_INSERT },
|
|
{ Key.Delete, (int)VK_DELETE },
|
|
{ Key.Help, (int)VK_HELP },
|
|
{ Key.D0, '0' },
|
|
{ Key.D1, '1' },
|
|
{ Key.D2, '2' },
|
|
{ Key.D3, '3' },
|
|
{ Key.D4, '4' },
|
|
{ Key.D5, '5' },
|
|
{ Key.D6, '6' },
|
|
{ Key.D7, '7' },
|
|
{ Key.D8, '8' },
|
|
{ Key.D9, '9' },
|
|
{ Key.A, 'A' },
|
|
{ Key.B, 'B' },
|
|
{ Key.C, 'C' },
|
|
{ Key.D, 'D' },
|
|
{ Key.E, 'E' },
|
|
{ Key.F, 'F' },
|
|
{ Key.G, 'G' },
|
|
{ Key.H, 'H' },
|
|
{ Key.I, 'I' },
|
|
{ Key.J, 'J' },
|
|
{ Key.K, 'K' },
|
|
{ Key.L, 'L' },
|
|
{ Key.M, 'M' },
|
|
{ Key.N, 'N' },
|
|
{ Key.O, 'O' },
|
|
{ Key.P, 'P' },
|
|
{ Key.Q, 'Q' },
|
|
{ Key.R, 'R' },
|
|
{ Key.S, 'S' },
|
|
{ Key.T, 'T' },
|
|
{ Key.U, 'U' },
|
|
{ Key.V, 'V' },
|
|
{ Key.W, 'W' },
|
|
{ Key.X, 'X' },
|
|
{ Key.Y, 'Y' },
|
|
{ Key.Z, 'Z' },
|
|
{ Key.LWin, (int)VK_LWIN },
|
|
{ Key.RWin, (int)VK_RWIN },
|
|
{ Key.Apps, (int)VK_APPS },
|
|
{ Key.Sleep, (int)VK_SLEEP },
|
|
{ Key.NumPad0, (int)VK_NUMPAD0 },
|
|
{ Key.NumPad1, (int)VK_NUMPAD1 },
|
|
{ Key.NumPad2, (int)VK_NUMPAD2 },
|
|
{ Key.NumPad3, (int)VK_NUMPAD3 },
|
|
{ Key.NumPad4, (int)VK_NUMPAD4 },
|
|
{ Key.NumPad5, (int)VK_NUMPAD5 },
|
|
{ Key.NumPad6, (int)VK_NUMPAD6 },
|
|
{ Key.NumPad7, (int)VK_NUMPAD7 },
|
|
{ Key.NumPad8, (int)VK_NUMPAD8 },
|
|
{ Key.NumPad9, (int)VK_NUMPAD9 },
|
|
{ Key.Multiply, (int)VK_MULTIPLY },
|
|
{ Key.Add, (int)VK_ADD },
|
|
{ Key.Separator, (int)VK_SEPARATOR },
|
|
{ Key.Subtract, (int)VK_SUBTRACT },
|
|
{ Key.Decimal, (int)VK_DECIMAL },
|
|
{ Key.Divide, (int)VK_DIVIDE },
|
|
{ Key.F1, (int)VK_F1 },
|
|
{ Key.F2, (int)VK_F2 },
|
|
{ Key.F3, (int)VK_F3 },
|
|
{ Key.F4, (int)VK_F4 },
|
|
{ Key.F5, (int)VK_F5 },
|
|
{ Key.F6, (int)VK_F6 },
|
|
{ Key.F7, (int)VK_F7 },
|
|
{ Key.F8, (int)VK_F8 },
|
|
{ Key.F9, (int)VK_F9 },
|
|
{ Key.F10, (int)VK_F10 },
|
|
{ Key.F11, (int)VK_F11 },
|
|
{ Key.F12, (int)VK_F12 },
|
|
{ Key.F13, (int)VK_F13 },
|
|
{ Key.F14, (int)VK_F14 },
|
|
{ Key.F15, (int)VK_F15 },
|
|
{ Key.F16, (int)VK_F16 },
|
|
{ Key.F17, (int)VK_F17 },
|
|
{ Key.F18, (int)VK_F18 },
|
|
{ Key.F19, (int)VK_F19 },
|
|
{ Key.F20, (int)VK_F20 },
|
|
{ Key.F21, (int)VK_F21 },
|
|
{ Key.F22, (int)VK_F22 },
|
|
{ Key.F23, (int)VK_F23 },
|
|
{ Key.F24, (int)VK_F24 },
|
|
{ Key.NumLock, (int)VK_NUMLOCK },
|
|
{ Key.Scroll, (int)VK_SCROLL },
|
|
{ Key.LeftShift, (int)VK_LSHIFT },
|
|
{ Key.RightShift, (int)VK_RSHIFT },
|
|
{ Key.LeftCtrl, (int)VK_LCONTROL },
|
|
{ Key.RightCtrl, (int)VK_RCONTROL },
|
|
{ Key.LeftAlt, (int)VK_LMENU },
|
|
{ Key.RightAlt, (int)VK_RMENU },
|
|
{ Key.BrowserBack, (int)VK_BROWSER_BACK },
|
|
{ Key.BrowserForward, (int)VK_BROWSER_FORWARD },
|
|
{ Key.BrowserRefresh, (int)VK_BROWSER_REFRESH },
|
|
{ Key.BrowserStop, (int)VK_BROWSER_STOP },
|
|
{ Key.BrowserSearch, (int)VK_BROWSER_SEARCH },
|
|
{ Key.BrowserFavorites, (int)VK_BROWSER_FAVORITES },
|
|
{ Key.BrowserHome, (int)VK_BROWSER_HOME },
|
|
{ Key.VolumeMute, (int)VK_VOLUME_MUTE },
|
|
{ Key.VolumeDown, (int)VK_VOLUME_DOWN },
|
|
{ Key.VolumeUp, (int)VK_VOLUME_UP },
|
|
{ Key.MediaNextTrack, (int)VK_MEDIA_NEXT_TRACK },
|
|
{ Key.MediaPreviousTrack, (int)VK_MEDIA_PREV_TRACK },
|
|
{ Key.MediaStop, (int)VK_MEDIA_STOP },
|
|
{ Key.MediaPlayPause, (int)VK_MEDIA_PLAY_PAUSE },
|
|
{ Key.LaunchMail, (int)VK_LAUNCH_MAIL },
|
|
{ Key.SelectMedia, (int)VK_LAUNCH_MEDIA_SELECT },
|
|
{ Key.LaunchApplication1, (int)VK_LAUNCH_APP1 },
|
|
{ Key.LaunchApplication2, (int)VK_LAUNCH_APP2 },
|
|
{ Key.Oem1, (int)VK_OEM_1 },
|
|
{ Key.OemPlus, (int)VK_OEM_PLUS },
|
|
{ Key.OemComma, (int)VK_OEM_COMMA },
|
|
{ Key.OemMinus, (int)VK_OEM_MINUS },
|
|
{ Key.OemPeriod, (int)VK_OEM_PERIOD },
|
|
{ Key.OemQuestion, (int)VK_OEM_2 },
|
|
{ Key.Oem3, (int)VK_OEM_3 },
|
|
{ Key.AbntC1, (int)VK_ABNT_C1 },
|
|
{ Key.AbntC2, (int)VK_ABNT_C2 },
|
|
{ Key.OemOpenBrackets, (int)VK_OEM_4 },
|
|
{ Key.Oem5, (int)VK_OEM_5 },
|
|
{ Key.Oem6, (int)VK_OEM_6 },
|
|
{ Key.OemQuotes, (int)VK_OEM_7 },
|
|
{ Key.Oem8, (int)VK_OEM_8 },
|
|
{ Key.OemBackslash, (int)VK_OEM_102 },
|
|
{ Key.ImeProcessed, (int)VK_PROCESSKEY },
|
|
{ Key.OemAttn, (int)VK_OEM_ATTN },
|
|
{ Key.OemFinish, (int)VK_OEM_FINISH },
|
|
{ Key.OemCopy, (int)VK_OEM_COPY },
|
|
{ Key.DbeSbcsChar, (int)VK_OEM_AUTO },
|
|
{ Key.OemEnlw, (int)VK_OEM_ENLW },
|
|
{ Key.OemBackTab, (int)VK_OEM_BACKTAB },
|
|
{ Key.DbeNoRoman, (int)VK_ATTN },
|
|
{ Key.DbeEnterWordRegisterMode, (int)VK_CRSEL },
|
|
{ Key.DbeEnterImeConfigureMode, (int)VK_EXSEL },
|
|
{ Key.EraseEof, (int)VK_EREOF },
|
|
{ Key.Play, (int)VK_PLAY },
|
|
{ Key.DbeNoCodeInput, (int)VK_ZOOM },
|
|
{ Key.NoName, (int)VK_NONAME },
|
|
{ Key.Pa1, (int)VK_PA1 },
|
|
{ Key.OemClear, (int)VK_OEM_CLEAR }
|
|
};
|
|
|
|
private static readonly Dictionary<int, Key> s_keyFromVirtualKey =
|
|
s_virtualKeyFromKey.ToDictionary(pair => pair.Value, pair => pair.Key);
|
|
|
|
// https://learn.microsoft.com/en-us/windows/win32/inputdev/about-keyboard-input#scan-codes
|
|
// https://github.com/chromium/chromium/blob/main/ui/events/keycodes/dom/dom_code_data.inc
|
|
// This list has the same order as the PhysicalKey enum.
|
|
private static readonly Dictionary<ushort, PhysicalKey> s_physicalKeyFromExtendedScanCode = new(155)
|
|
{
|
|
// Writing System Keys
|
|
{ 0x0029, PhysicalKey.Backquote },
|
|
{ 0x002B, PhysicalKey.Backslash },
|
|
{ 0x001A, PhysicalKey.BracketLeft },
|
|
{ 0x001B, PhysicalKey.BracketRight },
|
|
{ 0x0033, PhysicalKey.Comma },
|
|
{ 0x000B, PhysicalKey.Digit0 },
|
|
{ 0x0002, PhysicalKey.Digit1 },
|
|
{ 0x0003, PhysicalKey.Digit2 },
|
|
{ 0x0004, PhysicalKey.Digit3 },
|
|
{ 0x0005, PhysicalKey.Digit4 },
|
|
{ 0x0006, PhysicalKey.Digit5 },
|
|
{ 0x0007, PhysicalKey.Digit6 },
|
|
{ 0x0008, PhysicalKey.Digit7 },
|
|
{ 0x0009, PhysicalKey.Digit8 },
|
|
{ 0x000A, PhysicalKey.Digit9 },
|
|
{ 0x000D, PhysicalKey.Equal },
|
|
{ 0x0056, PhysicalKey.IntlBackslash },
|
|
{ 0x0073, PhysicalKey.IntlRo },
|
|
{ 0x007D, PhysicalKey.IntlYen },
|
|
{ 0x001E, PhysicalKey.A },
|
|
{ 0x0030, PhysicalKey.B },
|
|
{ 0x002E, PhysicalKey.C },
|
|
{ 0x0020, PhysicalKey.D },
|
|
{ 0x0012, PhysicalKey.E },
|
|
{ 0x0021, PhysicalKey.F },
|
|
{ 0x0022, PhysicalKey.G },
|
|
{ 0x0023, PhysicalKey.H },
|
|
{ 0x0017, PhysicalKey.I },
|
|
{ 0x0024, PhysicalKey.J },
|
|
{ 0x0025, PhysicalKey.K },
|
|
{ 0x0026, PhysicalKey.L },
|
|
{ 0x0032, PhysicalKey.M },
|
|
{ 0x0031, PhysicalKey.N },
|
|
{ 0x0018, PhysicalKey.O },
|
|
{ 0x0019, PhysicalKey.P },
|
|
{ 0x0010, PhysicalKey.Q },
|
|
{ 0x0013, PhysicalKey.R },
|
|
{ 0x001F, PhysicalKey.S },
|
|
{ 0x0014, PhysicalKey.T },
|
|
{ 0x0016, PhysicalKey.U },
|
|
{ 0x002F, PhysicalKey.V },
|
|
{ 0x0011, PhysicalKey.W },
|
|
{ 0x002D, PhysicalKey.X },
|
|
{ 0x0015, PhysicalKey.Y },
|
|
{ 0x002C, PhysicalKey.Z },
|
|
{ 0x000C, PhysicalKey.Minus },
|
|
{ 0x0034, PhysicalKey.Period },
|
|
{ 0x0028, PhysicalKey.Quote },
|
|
{ 0x0027, PhysicalKey.Semicolon },
|
|
{ 0x0035, PhysicalKey.Slash },
|
|
|
|
// Functional Keys
|
|
{ 0x0038, PhysicalKey.AltLeft },
|
|
{ 0xE038, PhysicalKey.AltRight },
|
|
{ 0x000E, PhysicalKey.Backspace },
|
|
{ 0x003A, PhysicalKey.CapsLock },
|
|
{ 0xE05D, PhysicalKey.ContextMenu },
|
|
{ 0x001D, PhysicalKey.ControlLeft },
|
|
{ 0xE01D, PhysicalKey.ControlRight },
|
|
{ 0x001C, PhysicalKey.Enter },
|
|
{ 0xE05B, PhysicalKey.MetaLeft },
|
|
{ 0xE05C, PhysicalKey.MetaRight },
|
|
{ 0x002A, PhysicalKey.ShiftLeft },
|
|
{ 0x0036, PhysicalKey.ShiftRight },
|
|
{ 0x0039, PhysicalKey.Space },
|
|
{ 0x000F, PhysicalKey.Tab },
|
|
{ 0x0079, PhysicalKey.Convert },
|
|
{ 0x0070, PhysicalKey.KanaMode },
|
|
{ 0x0072, PhysicalKey.Lang1 },
|
|
{ 0x0071, PhysicalKey.Lang2 },
|
|
{ 0x0078, PhysicalKey.Lang3 },
|
|
{ 0x0077, PhysicalKey.Lang4 },
|
|
//{ , PhysicalKey.Lang5 }, Not mapped on Windows since it's the same as F24 (see Chromium remarks)
|
|
{ 0x007B, PhysicalKey.NonConvert },
|
|
|
|
// Control Pad Section
|
|
{ 0xE053, PhysicalKey.Delete },
|
|
{ 0xE04F, PhysicalKey.End },
|
|
{ 0xE03B, PhysicalKey.Help },
|
|
{ 0xE047, PhysicalKey.Home },
|
|
{ 0xE052, PhysicalKey.Insert },
|
|
{ 0xE051, PhysicalKey.PageDown },
|
|
{ 0xE049, PhysicalKey.PageUp },
|
|
|
|
// Arrow Pad Section
|
|
{ 0xE050, PhysicalKey.ArrowDown },
|
|
{ 0xE04B, PhysicalKey.ArrowLeft },
|
|
{ 0xE04D, PhysicalKey.ArrowRight },
|
|
{ 0xE048, PhysicalKey.ArrowUp },
|
|
|
|
// Numpad Section
|
|
{ 0xE045, PhysicalKey.NumLock },
|
|
{ 0x0052, PhysicalKey.NumPad0 },
|
|
{ 0x004F, PhysicalKey.NumPad1 },
|
|
{ 0x0050, PhysicalKey.NumPad2 },
|
|
{ 0x0051, PhysicalKey.NumPad3 },
|
|
{ 0x004B, PhysicalKey.NumPad4 },
|
|
{ 0x004C, PhysicalKey.NumPad5 },
|
|
{ 0x004D, PhysicalKey.NumPad6 },
|
|
{ 0x0047, PhysicalKey.NumPad7 },
|
|
{ 0x0048, PhysicalKey.NumPad8 },
|
|
{ 0x0049, PhysicalKey.NumPad9 },
|
|
{ 0x004E, PhysicalKey.NumPadAdd },
|
|
//{ , PhysicalKey.NumPadClear },
|
|
{ 0x007E, PhysicalKey.NumPadComma },
|
|
{ 0x0053, PhysicalKey.NumPadDecimal },
|
|
{ 0xE035, PhysicalKey.NumPadDivide },
|
|
{ 0xE01C, PhysicalKey.NumPadEnter },
|
|
{ 0x0059, PhysicalKey.NumPadEqual },
|
|
{ 0x0037, PhysicalKey.NumPadMultiply },
|
|
//{ , PhysicalKey.NumPadParenLeft },
|
|
//{ , PhysicalKey.NumPadParenRight },
|
|
{ 0x004A, PhysicalKey.NumPadSubtract },
|
|
|
|
// Function Section
|
|
{ 0x0001, PhysicalKey.Escape },
|
|
{ 0x003B, PhysicalKey.F1 },
|
|
{ 0x003C, PhysicalKey.F2 },
|
|
{ 0x003D, PhysicalKey.F3 },
|
|
{ 0x003E, PhysicalKey.F4 },
|
|
{ 0x003F, PhysicalKey.F5 },
|
|
{ 0x0040, PhysicalKey.F6 },
|
|
{ 0x0041, PhysicalKey.F7 },
|
|
{ 0x0042, PhysicalKey.F8 },
|
|
{ 0x0043, PhysicalKey.F9 },
|
|
{ 0x0044, PhysicalKey.F10 },
|
|
{ 0x0057, PhysicalKey.F11 },
|
|
{ 0x0058, PhysicalKey.F12 },
|
|
{ 0x0064, PhysicalKey.F13 },
|
|
{ 0x0065, PhysicalKey.F14 },
|
|
{ 0x0066, PhysicalKey.F15 },
|
|
{ 0x0067, PhysicalKey.F16 },
|
|
{ 0x0068, PhysicalKey.F17 },
|
|
{ 0x0069, PhysicalKey.F18 },
|
|
{ 0x006A, PhysicalKey.F19 },
|
|
{ 0x006B, PhysicalKey.F20 },
|
|
{ 0x006C, PhysicalKey.F21 },
|
|
{ 0x006D, PhysicalKey.F22 },
|
|
{ 0x006E, PhysicalKey.F23 },
|
|
{ 0x0076, PhysicalKey.F24 },
|
|
{ 0xE037, PhysicalKey.PrintScreen },
|
|
{ 0x0046, PhysicalKey.ScrollLock },
|
|
{ 0x0045, PhysicalKey.Pause },
|
|
|
|
// Media Keys
|
|
{ 0xE06A, PhysicalKey.BrowserBack },
|
|
{ 0xE066, PhysicalKey.BrowserFavorites },
|
|
{ 0xE069, PhysicalKey.BrowserForward },
|
|
{ 0xE032, PhysicalKey.BrowserHome },
|
|
{ 0xE067, PhysicalKey.BrowserRefresh },
|
|
{ 0xE065, PhysicalKey.BrowserSearch },
|
|
{ 0xE068, PhysicalKey.BrowserStop },
|
|
{ 0xE02C, PhysicalKey.Eject },
|
|
{ 0xE06B, PhysicalKey.LaunchApp1 },
|
|
{ 0xE021, PhysicalKey.LaunchApp2 },
|
|
{ 0xE06C, PhysicalKey.LaunchMail },
|
|
{ 0xE022, PhysicalKey.MediaPlayPause },
|
|
{ 0xE06D, PhysicalKey.MediaSelect },
|
|
{ 0xE024, PhysicalKey.MediaStop },
|
|
{ 0xE019, PhysicalKey.MediaTrackNext },
|
|
{ 0xE010, PhysicalKey.MediaTrackPrevious },
|
|
{ 0xE05E, PhysicalKey.Power },
|
|
{ 0xE05F, PhysicalKey.Sleep },
|
|
{ 0xE02E, PhysicalKey.AudioVolumeDown },
|
|
{ 0xE020, PhysicalKey.AudioVolumeMute },
|
|
{ 0xE030, PhysicalKey.AudioVolumeUp },
|
|
{ 0xE063, PhysicalKey.WakeUp },
|
|
|
|
// Legacy Keys
|
|
{ 0xE018, PhysicalKey.Copy },
|
|
{ 0xE017, PhysicalKey.Cut },
|
|
//{ , PhysicalKey.Find },
|
|
//{ , PhysicalKey.Open },
|
|
{ 0xE00A, PhysicalKey.Paste },
|
|
//{ , PhysicalKey.Props },
|
|
//{ , PhysicalKey.Select },
|
|
{ 0xE008, PhysicalKey.Undo },
|
|
};
|
|
|
|
/// <summary>
|
|
/// Indicates whether the key is an extended key, such as the right-hand ALT and CTRL keys.
|
|
/// According to https://docs.microsoft.com/en-us/windows/win32/inputdev/wm-keydown.
|
|
/// </summary>
|
|
private static bool IsExtended(int keyData)
|
|
{
|
|
const int extendedMask = 1 << 24;
|
|
|
|
return (keyData & extendedMask) != 0;
|
|
}
|
|
|
|
private static byte GetScanCode(int keyData)
|
|
{
|
|
// Bits from 16 to 23 represent scan code.
|
|
const int scanCodeMask = 0xFF0000;
|
|
|
|
return (byte)((keyData & scanCodeMask) >> 16);
|
|
}
|
|
|
|
private static int GetVirtualKey(int virtualKey, int keyData)
|
|
{
|
|
// Adapted from https://github.com/dotnet/wpf/blob/master/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/InterOp/HwndKeyboardInputProvider.cs.
|
|
|
|
if (virtualKey == (int)VK_SHIFT)
|
|
{
|
|
var scanCode = GetScanCode(keyData);
|
|
|
|
virtualKey = (int)MapVirtualKey(scanCode, (uint)MapVirtualKeyMapTypes.MAPVK_VSC_TO_VK_EX);
|
|
|
|
if (virtualKey == 0)
|
|
{
|
|
virtualKey = (int)VK_LSHIFT;
|
|
}
|
|
}
|
|
|
|
else if (virtualKey == (int)VK_MENU)
|
|
{
|
|
bool isRight = IsExtended(keyData);
|
|
|
|
if (isRight)
|
|
{
|
|
virtualKey = (int)VK_RMENU;
|
|
}
|
|
else
|
|
{
|
|
virtualKey = (int)VK_LMENU;
|
|
}
|
|
}
|
|
|
|
else if (virtualKey == (int)VK_CONTROL)
|
|
{
|
|
bool isRight = IsExtended(keyData);
|
|
|
|
if (isRight)
|
|
{
|
|
virtualKey = (int)VK_RCONTROL;
|
|
}
|
|
else
|
|
{
|
|
virtualKey = (int)VK_LCONTROL;
|
|
}
|
|
}
|
|
|
|
return virtualKey;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets an Avalonia key from a Windows virtual-key and key data.
|
|
/// </summary>
|
|
/// <param name="virtualKey">The Windows virtual-key.</param>
|
|
/// <param name="keyData">The key data (in the same format as lParam for WM_KEYDOWN).</param>
|
|
/// <returns>An Avalonia key, or <see cref="Key.None"/> if none matched.</returns>
|
|
public static Key KeyFromVirtualKey(int virtualKey, int keyData)
|
|
{
|
|
virtualKey = GetVirtualKey(virtualKey, keyData);
|
|
|
|
s_keyFromVirtualKey.TryGetValue(virtualKey, out var result);
|
|
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a Windows virtual-key from an Avalonia key.
|
|
/// </summary>
|
|
/// <param name="key">The Avalonia key.</param>
|
|
/// <returns>A Windows virtual-key code, or 0 if none matched.</returns>
|
|
public static int VirtualKeyFromKey(Key key)
|
|
{
|
|
s_virtualKeyFromKey.TryGetValue(key, out var result);
|
|
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a physical Avalonia key from a Windows virtual-key and key data.
|
|
/// </summary>
|
|
/// <param name="virtualKey">The Windows virtual-key.</param>
|
|
/// <param name="keyData">The key data (in the same format as lParam for WM_KEYDOWN).</param>
|
|
/// <returns>An Avalonia physical key, or <see cref="PhysicalKey.None"/> if none matched.</returns>
|
|
public static PhysicalKey PhysicalKeyFromVirtualKey(int virtualKey, int keyData)
|
|
{
|
|
uint scanCode = GetScanCode(keyData);
|
|
if (scanCode == 0U)
|
|
{
|
|
// in some cases, the scan code contained in the keyData might be zero:
|
|
// try to get one from the virtual key instead
|
|
scanCode = MapVirtualKey((uint)virtualKey, (uint)MapVirtualKeyMapTypes.MAPVK_VK_TO_VSC);
|
|
if (scanCode == 0U)
|
|
return PhysicalKey.None;
|
|
}
|
|
|
|
if (IsExtended(keyData))
|
|
scanCode |= 0xE000;
|
|
|
|
return scanCode is > 0 and <= 0xE0FF
|
|
&& s_physicalKeyFromExtendedScanCode.TryGetValue((ushort)scanCode, out var result) ?
|
|
result :
|
|
PhysicalKey.None;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a key symbol from a Windows virtual-key and key data.
|
|
/// </summary>
|
|
/// <param name="virtualKey">The Windows virtual-key.</param>
|
|
/// <param name="keyData">The key data (in the same format as lParam for WM_KEYDOWN).</param>
|
|
/// <returns>A key symbol, or null if none matched.</returns>
|
|
public static unsafe string? GetKeySymbol(int virtualKey, int keyData)
|
|
{
|
|
const int bufferSize = 4;
|
|
const uint doNotChangeKeyboardState = 1U << 2;
|
|
|
|
fixed (byte* keyStates = stackalloc byte[256])
|
|
fixed (char* buffer = stackalloc char[bufferSize])
|
|
{
|
|
GetKeyboardState(keyStates);
|
|
|
|
var length = ToUnicodeEx(
|
|
(uint)virtualKey,
|
|
GetScanCode(keyData),
|
|
keyStates,
|
|
buffer,
|
|
bufferSize,
|
|
doNotChangeKeyboardState,
|
|
GetKeyboardLayout(0));
|
|
|
|
return length switch
|
|
{
|
|
< 0 => new string(buffer, 0, -length), // dead key
|
|
0 => null,
|
|
1 when !KeySymbolHelper.IsAllowedAsciiKeySymbol(buffer[0]) => null,
|
|
2 when buffer[0] == buffer[1] => new string(buffer, 0, 1), // dead key second press repeats symbol
|
|
_ => new string(buffer, 0, length)
|
|
};
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|