You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Avalonia中NativeControlHost嵌入SDL2无法捕获SDL_KEYDOWN/SDL_KEYUP事件

问题描述

我有一个基于Avalonia开发的应用,希望将一个使用SDL2-CS wrapper开发的C# SDL2应用嵌入其中。我通过以下代码使用NativeControlHost获取句柄:

public class NativeEmbeddingControl : NativeControlHost
{
    public IntPtr Handle { get; private set; }

    protected override IPlatformHandle CreateNativeControlCore(IPlatformHandle parent)
    {
        var handle = base.CreateNativeControlCore(parent);
        Handle = handle.Handle;
        return handle;
    }
}

随后通过该句柄创建SDL2窗口:

IntPtr windowPtr = SDL.SDL_CreateWindowFrom(windowHandle);

SDL2应用的内容可正常显示,但调用SDL_PollEvent时无法获取SDL_KEYDOWN和SDL_KEYUP事件;而SDL2应用独立运行时一切功能正常。请问需要额外执行哪些操作,才能将按键上下事件传递给嵌入式SDL2应用?

解决方案

要让嵌入式SDL2窗口接收键盘事件,需要手动在Avalonia的NativeEmbeddingControl中处理键盘事件并转发给SDL2窗口,具体操作如下:

  • 重写NativeControlHost的键盘事件处理方法
    在你的NativeEmbeddingControl类中,重写OnKeyDown、OnKeyUp等方法,将Avalonia的键盘事件转换为SDL2格式后,通过SDL_PushEvent推送给SDL2窗口。示例代码:

    public class NativeEmbeddingControl : NativeControlHost
    {
        public IntPtr Handle { get; private set; }
        private IntPtr _sdlWindow;
    
        // 保存SDL窗口句柄
        public void SetSdlWindow(IntPtr sdlWindow)
        {
            _sdlWindow = sdlWindow;
        }
    
        protected override IPlatformHandle CreateNativeControlCore(IPlatformHandle parent)
        {
            var handle = base.CreateNativeControlCore(parent);
            Handle = handle.Handle;
            return handle;
        }
    
        protected override void OnKeyDown(KeyEventArgs e)
        {
            base.OnKeyDown(e);
            if (_sdlWindow == IntPtr.Zero) return;
    
            var sdlKey = ConvertAvaloniaKeyToSdl(e.Key);
            if (sdlKey != SDL.SDL_Keycode.SDLK_UNKNOWN)
            {
                var sdlEvent = new SDL.SDL_Event
                {
                    key = new SDL.SDL_KeyboardEvent
                    {
                        type = SDL.SDL_EventType.SDL_KEYDOWN,
                        windowID = SDL.SDL_GetWindowID(_sdlWindow),
                        keysym = new SDL.SDL_Keysym
                        {
                            sym = sdlKey,
                            mod = ConvertAvaloniaModifiersToSdl(e.KeyModifiers)
                        }
                    }
                };
                SDL.SDL_PushEvent(ref sdlEvent);
                e.Handled = true; // 标记事件已处理,避免Avalonia继续拦截
            }
        }
    
        protected override void OnKeyUp(KeyEventArgs e)
        {
            base.OnKeyUp(e);
            if (_sdlWindow == IntPtr.Zero) return;
    
            var sdlKey = ConvertAvaloniaKeyToSdl(e.Key);
            if (sdlKey != SDL.SDL_Keycode.SDLK_UNKNOWN)
            {
                var sdlEvent = new SDL.SDL_Event
                {
                    key = new SDL.SDL_KeyboardEvent
                    {
                        type = SDL.SDL_EventType.SDL_KEYUP,
                        windowID = SDL.SDL_GetWindowID(_sdlWindow),
                        keysym = new SDL.SDL_Keysym
                        {
                            sym = sdlKey,
                            mod = ConvertAvaloniaModifiersToSdl(e.KeyModifiers)
                        }
                    }
                };
                SDL.SDL_PushEvent(ref sdlEvent);
                e.Handled = true;
            }
        }
    
        // 转换Avalonia按键到SDL按键码,需补充所有需要支持的按键映射
        private SDL.SDL_Keycode ConvertAvaloniaKeyToSdl(Key avaloniaKey)
        {
            return avaloniaKey switch
            {
                Key.A => SDL.SDL_Keycode.SDLK_a,
                Key.B => SDL.SDL_Keycode.SDLK_b,
                Key.Left => SDL.SDL_Keycode.SDLK_LEFT,
                Key.Right => SDL.SDL_Keycode.SDLK_RIGHT,
                Key.Up => SDL.SDL_Keycode.SDLK_UP,
                Key.Down => SDL.SDL_Keycode.SDLK_DOWN,
                Key.Space => SDL.SDL_Keycode.SDLK_SPACE,
                _ => SDL.SDL_Keycode.SDLK_UNKNOWN
            };
        }
    
        // 转换Avalonia修饰键到SDL修饰符
        private SDL.SDL_Keymod ConvertAvaloniaModifiersToSdl(KeyModifiers modifiers)
        {
            SDL.SDL_Keymod sdlMod = 0;
            if ((modifiers & KeyModifiers.Control) != 0)
                sdlMod |= SDL.SDL_Keymod.KMOD_CTRL;
            if ((modifiers & KeyModifiers.Shift) != 0)
                sdlMod |= SDL.SDL_Keymod.KMOD_SHIFT;
            if ((modifiers & KeyModifiers.Alt) != 0)
                sdlMod |= SDL.SDL_Keymod.KMOD_ALT;
            return sdlMod;
        }
    }
    
  • 关联SDL窗口句柄
    创建SDL窗口后,调用SetSdlWindow方法将SDL窗口句柄传递给NativeEmbeddingControl:

    IntPtr windowPtr = SDL.SDL_CreateWindowFrom(windowHandle);
    nativeEmbeddingControl.SetSdlWindow(windowPtr);
    
  • 确保控件获得焦点
    在控件加载完成时调用Focus()方法,确保键盘事件能被NativeEmbeddingControl捕获:

    nativeEmbeddingControl.Focus();
    

注意:如果你的SDL应用使用了SDL_SetEventFilter,需确保过滤器不会丢弃手动推送的键盘事件。

内容的提问来源于stack exchange,提问作者gryz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 11:52:38