XNA键盘西里尔语输入

时间:2013-03-29 09:25:51

标签: c# encoding xna keyboard-hook

我希望增加在XNA游戏中输入西里尔字母的可能性。 我不能使用默认的Input.KeyboardState,因为Input.Keys枚举只包含英文字母。所以,我使用了keyboard-hook来处理Windows输入。但它返回键码,可以用于英文字母(因为ASCII码与键码相匹配),但不适用于西里尔语。对于“a-я”可以使用848偏移量,但对于更具体的字母,如“і”或“ї”,则有不同的值。

有没有有效的方法将密钥代码转换为ASCII格式的西里尔字母?或者也许有没有令人讨厌的键盘钩子的解决方案?

顺便说一下,这里的键盘钩类列表:

#region EventArgs

public class CharacterEventArgs : EventArgs
{
    private readonly char character;
    private readonly int lParam;
    private readonly int keyLayout;

    public CharacterEventArgs(char character, int lParam, int keyLayout)
    {
        this.character = character;
        this.lParam = lParam;
        this.keyLayout = keyLayout;
    }

    #region Properties

    public int KeyLayout
    {
        get { return keyLayout; }
    }

    public char Character
    {
        get { return character; }
    }

    public int Param
    {
        get { return lParam; }
    }

    public int RepeatCount
    {
        get { return lParam & 0xffff; }
    }

    public bool ExtendedKey
    {
        get { return (lParam & (1 << 24)) > 0; }
    }

    public bool AltPressed
    {
        get { return (lParam & (1 << 29)) > 0; }
    }

    public bool PreviousState
    {
        get { return (lParam & (1 << 30)) > 0; }
    }

    public bool TransitionState
    {
        get { return (lParam & (1 << 31)) > 0; }
    }

    #endregion
}

public class KeyEventArgs : EventArgs
{
    private Keys keyCode;

    public KeyEventArgs(Keys keyCode)
    {
        this.keyCode = keyCode;
    }

    public Keys KeyCode
    {
        get { return keyCode; }
    }
}

#endregion

#region delegats

public delegate void CharEnteredHandler(object sender, CharacterEventArgs e);
public delegate void KeyEventHandler(object sender, KeyEventArgs e);

#endregion

public static class EventInput
{
    #region events

    /// <summary>
    /// Event raised when a character has been entered.
    /// </summary>
    public static event CharEnteredHandler CharEntered;

    /// <summary>
    /// Event raised when a key has been pressed down. May fire multiple times due to keyboard repeat.
    /// </summary>
    public static event KeyEventHandler KeyDown;

    /// <summary>
    /// Event raised when a key has been released.
    /// </summary>
    public static event KeyEventHandler KeyUp;

    #endregion

    delegate IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);

    static bool initialized;
    static IntPtr prevWndProc;
    static WndProc hookProcDelegate;
    static IntPtr hIMC;

    static GameWindow gameWindow;

    #region constants

    const int GWL_WNDPROC = -4;
    const int WM_KEYDOWN = 0x100;
    const int WM_KEYUP = 0x101;
    const int WM_CHAR = 0x102;
    const int WM_IME_SETCONTEXT = 0x0281;
    const int WM_INPUTLANGCHANGE = 0x51;
    const int WM_GETDLGCODE = 0x87;
    const int WM_IME_COMPOSITION = 0x10f;
    const int DLGC_WANTALLKEYS = 4;

    #endregion

    #region Win32-functions

    //to handle input

    [DllImport("Imm32.dll")]
    static extern IntPtr ImmGetContext(IntPtr hWnd);

    [DllImport("Imm32.dll")]
    static extern IntPtr ImmAssociateContext(IntPtr hWnd, IntPtr hIMC);

    [DllImport("user32.dll")]
    static extern IntPtr CallWindowProc(IntPtr lpPrevWndFunc, IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);

    [DllImport("user32.dll")]
    static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);


    //to get keyboard layout

    [DllImport("user32.dll", SetLastError = true)]
    static extern int GetWindowThreadProcessId(
        [In] IntPtr hWnd,
        [Out, Optional] IntPtr lpdwProcessId
        );

    [DllImport("user32.dll", SetLastError = true)]
    static extern ushort GetKeyboardLayout(
        [In] int idThread
        );

    #endregion

    static ushort GetKeyboardLayout()
    {
        return GetKeyboardLayout(GetWindowThreadProcessId(gameWindow.Handle, IntPtr.Zero));
    }

    #region Initialize

    /// <summary>
    /// Initialize the TextInput with the given GameWindow.
    /// </summary>
    /// <param name="window">The XNA window to which text input should be linked.</param>
    public static void Initialize(GameWindow window)
    {
        if (initialized)
            throw new InvalidOperationException("TextInput.Initialize can only be called once!");

        gameWindow = window;

        hookProcDelegate = new WndProc(HookProc);
        prevWndProc = (IntPtr)SetWindowLong(window.Handle, GWL_WNDPROC,
            (int)Marshal.GetFunctionPointerForDelegate(hookProcDelegate));

        hIMC = ImmGetContext(window.Handle);
        initialized = true;
    }

    #endregion

    static IntPtr HookProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
    {
        IntPtr returnCode = CallWindowProc(prevWndProc, hWnd, msg, wParam, lParam);

        switch (msg)
        {
            case WM_GETDLGCODE:
                returnCode = (IntPtr)(returnCode.ToInt32() | DLGC_WANTALLKEYS);
                break;

            case WM_KEYDOWN:
                if (KeyDown != null)
                    KeyDown(null, new KeyEventArgs((Keys)wParam));
                break;

            case WM_KEYUP:
                if (KeyUp != null)
                    KeyUp(null, new KeyEventArgs((Keys)wParam));
                break;

            case WM_CHAR:
                if (CharEntered != null)
                {
                    CharEntered(null, new CharacterEventArgs((char) wParam, lParam.ToInt32(), GetKeyboardLayout()));
                }
                break;

            case WM_IME_SETCONTEXT:
                if (wParam.ToInt32() == 1)
                    ImmAssociateContext(hWnd, hIMC);
                break;

            case WM_INPUTLANGCHANGE:
                ImmAssociateContext(hWnd, hIMC);
                returnCode = (IntPtr)1;
                break;
        }

        return returnCode;
    }
}

1 个答案:

答案 0 :(得分:2)

您是否考虑过使用Windows窗体进行字符输入?我不会说俄语,但我只能假设KeyPress事件必须在俄语语言环境中正确处理Cyrillic,这比自己抽取Windows消息简单得多。

using System.Windows.Forms;

...

var form = (Form)Form.FromHandle(window.Handle);
form.KeyPress += form_KeyPress;

...

private void form_KeyPress(Object sender, KeyPressEventArgs e)
{  
    Console.WriteLine(e.KeyChar);
    e.Handled = true;
}