PixiEditor/src/PixiEditor.Windows/WindowsInputKeys.cs
2025-05-16 14:45:31 +02:00

83 lines
2.9 KiB
C#

using System.Text;
using Avalonia.Input;
using Avalonia.Win32.Input;
using PixiEditor.OperatingSystem;
namespace PixiEditor.Windows;
public class WindowsInputKeys : IInputKeys
{
const string Russian = "00000419";
const string Ukrainian = "00000422";
const string UkrainianEnhanced = "00020422";
const string Arabic1 = "00000401";
const string Arabic2 = "00010401";
const string Arabic3 = "00020401";
private const string InvariantLayoutCode = "00000409"; // Also known as the US Layout
private static nint? invariantLayout;
/// <summary>
/// Returns the character of the <paramref name="key"/> mapped to the users keyboard layout
/// </summary>
public string GetKeyboardKey(Key key, bool forceInvariant = false) => key switch
{
>= Key.NumPad0 and <= Key.Divide => $"Num {GetMappedKey(key, forceInvariant)}",
Key.Space => nameof(Key.Space),
Key.Tab => nameof(Key.Tab),
Key.Return => "↵",
Key.Back => "Backspace",
Key.Escape => "Esc",
_ => GetMappedKey(key, forceInvariant),
};
public bool ModifierUsesSymbol(KeyModifiers modifier)
{
return false;
}
private static string GetMappedKey(Key key, bool forceInvariant)
{
int virtualKey = KeyInterop.VirtualKeyFromKey(key);
byte[] keyboardState = new byte[256];
nint targetLayout = GetLayoutHkl(forceInvariant);
uint scanCode = Win32.MapVirtualKeyExW((uint)virtualKey, Win32.MapType.MAPVK_VK_TO_VSC, targetLayout);
StringBuilder stringBuilder = new(5);
int result = Win32.ToUnicodeEx((uint)virtualKey, scanCode, keyboardState, stringBuilder, stringBuilder.Capacity, 0, targetLayout);
string stringResult = result switch
{
0 => key.ToString(),
-1 => stringBuilder.ToString().ToUpper(),
_ => stringBuilder[result - 1].ToString().ToUpper()
};
return stringResult;
}
private static nint GetLayoutHkl(bool forceInvariant = false)
{
if (forceInvariant)
{
invariantLayout ??= Win32.LoadKeyboardLayoutA(InvariantLayoutCode, 1);
return invariantLayout.Value;
}
var builder = new StringBuilder(8);
bool success = Win32.GetKeyboardLayoutNameW(builder);
// Fallback to US layout for certain layouts. Do not prepend a 0x and make sure the string is 8 chars long
// Layouts can be found here https://learn.microsoft.com/en-us/windows-hardware/manufacture/desktop/windows-language-pack-default-values?view=windows-11
if (!success || builder.ToString() is not (Russian or Ukrainian or UkrainianEnhanced or Arabic1 or Arabic2 or Arabic3))
{
return Win32.GetKeyboardLayout(0);
}
invariantLayout ??= Win32.LoadKeyboardLayoutA(InvariantLayoutCode, 1);
return invariantLayout.Value;
}
}