Mono 和 Gtk 中的全局热键#

发布于 2024-08-23 14:26:44 字数 1787 浏览 2 评论 0原文

我正在尝试使用 Mono 在 Linux 中使用全局热键。我找到了 XGrabKeyXUngrabKey 的签名,但我似乎无法让它们工作。每当我尝试调用 XGrabKey 时,应用程序都会崩溃并显示 SIGSEGV。

这就是我到目前为止所拥有的:

using System;
using Gtk;
using System.Runtime.InteropServices;

namespace GTKTest
{
    class MainClass
    {
        const int GrabModeAsync = 1;

        public static void Main(string[] args)
        {
            Application.Init();

            MainWindow win = new MainWindow();
            win.Show();

            // Crashes here
            XGrabKey(
             win.Display.Handle,
             (int)Gdk.Key.A,
             (uint)KeyMasks.ShiftMask,
             win.Handle,
             true,
             GrabModeAsync,
             GrabModeAsync);

            Application.Run();

            XUngrabKey(
             win.Display.Handle,
             (int)Gdk.Key.A,
             (uint)KeyMasks.ShiftMask,
             win.Handle);
        }


        [DllImport("libX11")]
        internal static extern int XGrabKey(
         IntPtr display,
         int keycode,
         uint modifiers,
         IntPtr grab_window,
         bool owner_events,
         int pointer_mode,
         int keyboard_mode);

        [DllImport("libX11")]
        internal static extern int XUngrabKey(
         IntPtr display,
         int keycode,
         uint modifiers,
         IntPtr grab_window);
    }

    public enum KeyMasks
    {
        ShiftMask = (1 << 0),
        LockMask = (1 << 1),
        ControlMask = (1 << 2),
        Mod1Mask = (1 << 3),
        Mod2Mask = (1 << 4),
        Mod3Mask = (1 << 5),
        Mod4Mask = (1 << 6),
        Mod5Mask = (1 << 7)
    }
}

有人有 XGrabKey 的工作示例吗?

谢谢!

I'm trying to get a global hotkey working in Linux using Mono. I found the signatures of XGrabKey and XUngrabKey, but I can't seem to get them working. Whenever I try to invoke XGrabKey, the application crashes with a SIGSEGV.

This is what I have so far:

using System;
using Gtk;
using System.Runtime.InteropServices;

namespace GTKTest
{
    class MainClass
    {
        const int GrabModeAsync = 1;

        public static void Main(string[] args)
        {
            Application.Init();

            MainWindow win = new MainWindow();
            win.Show();

            // Crashes here
            XGrabKey(
             win.Display.Handle,
             (int)Gdk.Key.A,
             (uint)KeyMasks.ShiftMask,
             win.Handle,
             true,
             GrabModeAsync,
             GrabModeAsync);

            Application.Run();

            XUngrabKey(
             win.Display.Handle,
             (int)Gdk.Key.A,
             (uint)KeyMasks.ShiftMask,
             win.Handle);
        }


        [DllImport("libX11")]
        internal static extern int XGrabKey(
         IntPtr display,
         int keycode,
         uint modifiers,
         IntPtr grab_window,
         bool owner_events,
         int pointer_mode,
         int keyboard_mode);

        [DllImport("libX11")]
        internal static extern int XUngrabKey(
         IntPtr display,
         int keycode,
         uint modifiers,
         IntPtr grab_window);
    }

    public enum KeyMasks
    {
        ShiftMask = (1 << 0),
        LockMask = (1 << 1),
        ControlMask = (1 << 2),
        Mod1Mask = (1 << 3),
        Mod2Mask = (1 << 4),
        Mod3Mask = (1 << 5),
        Mod4Mask = (1 << 6),
        Mod5Mask = (1 << 7)
    }
}

Does anyone have a working example of XGrabKey?

Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

空城旧梦 2024-08-30 14:26:44

好吧,我终于在托管代码中找到了一个可行的解决方案。发生 SIGSEGV 是因为我混淆了非托管 Gdk 对象的句柄与其 X11 对应对象的句柄。感谢 Paul 的回答,我能够找到全局热键的非托管示例,并熟悉它的工作原理。然后我编写了自己的非托管测试程序来找出我需要做什么,而不必处理任何托管特性。成功后,我创建了一个托管解决方案。

这是托管解决方案:

public class X11Hotkey
{
    private const int KeyPress = 2;
    private const int GrabModeAsync = 1;
    private Gdk.Key key;
    private Gdk.ModifierType modifiers;
    private int keycode;

    public X11Hotkey(Gdk.Key key, Gdk.ModifierType modifiers)
    {
        this.key = key;
        this.modifiers = modifiers;

        Gdk.Window rootWin = Gdk.Global.DefaultRootWindow;
        IntPtr xDisplay = GetXDisplay(rootWin);
        this.keycode = XKeysymToKeycode(xDisplay, (int)this.key);
        rootWin.AddFilter(new Gdk.FilterFunc(FilterFunction));
    }

    public event EventHandler Pressed;

    public void Register()
    {
        Gdk.Window rootWin = Gdk.Global.DefaultRootWindow;
        IntPtr xDisplay = GetXDisplay(rootWin);

        XGrabKey(
                 xDisplay,
                 this.keycode,
                 (uint)this.modifiers,
                 GetXWindow(rootWin),
                 false,
                 GrabModeAsync,
                 GrabModeAsync);     
    }

    public void Unregister()
    {
        Gdk.Window rootWin = Gdk.Global.DefaultRootWindow;
        IntPtr xDisplay = GetXDisplay(rootWin);

        XUngrabKey(
                 xDisplay,
                 this.keycode,
                 (uint)this.modifiers,
                 GetXWindow(rootWin));
    }

    private Gdk.FilterReturn FilterFunction(IntPtr xEvent, Gdk.Event evnt)
    {
        XKeyEvent xKeyEvent = (XKeyEvent)Marshal.PtrToStructure(
            xEvent, 
            typeof(XKeyEvent));

        if (xKeyEvent.type == KeyPress)
        {
            if (xKeyEvent.keycode == this.keycode 
                && xKeyEvent.state == (uint)this.modifiers)
            {
                this.OnPressed(EventArgs.Empty);
            }
        }

        return Gdk.FilterReturn.Continue;
    }

    protected virtual void OnPressed(EventArgs e)
    {
        EventHandler handler = this.Pressed;
        if (handler != null)
        {
            handler(this, e);
        }
    }

    private static IntPtr GetXWindow(Gdk.Window window)
    {
        return gdk_x11_drawable_get_xid(window.Handle);
    }

    private static IntPtr GetXDisplay(Gdk.Window window)
    {
        return gdk_x11_drawable_get_xdisplay(
            gdk_x11_window_get_drawable_impl(window.Handle));
    }

    [DllImport("libgtk-x11-2.0")]
    private static extern IntPtr gdk_x11_drawable_get_xid(IntPtr gdkWindow); 

    [DllImport("libgtk-x11-2.0")]
    private static extern IntPtr gdk_x11_drawable_get_xdisplay(IntPtr gdkDrawable);

    [DllImport("libgtk-x11-2.0")]
    private static extern IntPtr gdk_x11_window_get_drawable_impl(IntPtr gdkWindow);

    [DllImport("libX11")]
    private static extern int XKeysymToKeycode(IntPtr display, int key);

    [DllImport("libX11")]
    private static extern int XGrabKey(
        IntPtr display, 
        int keycode, 
        uint modifiers, 
        IntPtr grab_window, 
        bool owner_events, 
        int pointer_mode, 
        int keyboard_mode);

    [DllImport("libX11")]
    private static extern int XUngrabKey(
        IntPtr display, 
        int keycode, 
        uint modifiers, 
        IntPtr grab_window);

#if BUILD_FOR_32_BIT_X11        

    [StructLayout(LayoutKind.Sequential)]
    internal struct XKeyEvent
    {
        public short type;
        public uint serial;
        public short send_event;
        public IntPtr display;
        public uint window;
        public uint root;
        public uint subwindow;
        public uint time;
        public int x, y;
        public int x_root, y_root;
        public uint state;
        public uint keycode;
        public short same_screen;
    }       
#elif BUILD_FOR_64_BIT_X11

    [StructLayout(LayoutKind.Sequential)]
    internal struct XKeyEvent
    {
        public int type;
        public ulong serial;
        public int send_event;
        public IntPtr display;
        public ulong window;
        public ulong root;
        public ulong subwindow;
        public ulong time;
        public int x, y;
        public int x_root, y_root;
        public uint state;
        public uint keycode;
        public int same_screen;
    }
#endif      

}

这是测试程序:

public static void Main (string[] args)
{
    Application.Init();

    X11Hotkey hotkey = new X11Hotkey(Gdk.Key.A, Gdk.ModifierType.ControlMask);
    hotkey.Pressed += HotkeyPressed;;
    hotkey.Register();

    Application.Run();

    hotkey.Unregister();
}

private static void HotkeyPressed(object sender, EventArgs e)
{
    Console.WriteLine("Hotkey Pressed!");
}

我不确定 XKeyEvent 结构在具有不同大小的 C int 和 < code>longs,所以这个解决方案是否适用于所有系统还有待观察。

编辑:由于底层 C 类型大小的不同性质,看起来这个解决方案不会像我担心的那样独立于体系结构。 libgtkhotkey 看起来很有前途,可以避免使用托管程序集部署和编译自定义非托管库。

注意:现在您需要根据操作系统的字长明确定义BUILD_FOR_32_BIT_X11BUILD_FOR_64_BIT_X11

Well, I finally found a working solution in managed code. The SIGSEGV was happening because I was confusing the handles of the unmanaged Gdk objects with the handles of their X11 counterparts. Thanks to Paul's answer, I was able to find an unmanaged example of global hotkeys and familiarized myself with how it worked. Then I wrote my own unmanaged test program to find out what I needed to do without having to deal with any managed idiosyncrasies. After that was successful, I created a managed solution.

Here is the managed solution:

public class X11Hotkey
{
    private const int KeyPress = 2;
    private const int GrabModeAsync = 1;
    private Gdk.Key key;
    private Gdk.ModifierType modifiers;
    private int keycode;

    public X11Hotkey(Gdk.Key key, Gdk.ModifierType modifiers)
    {
        this.key = key;
        this.modifiers = modifiers;

        Gdk.Window rootWin = Gdk.Global.DefaultRootWindow;
        IntPtr xDisplay = GetXDisplay(rootWin);
        this.keycode = XKeysymToKeycode(xDisplay, (int)this.key);
        rootWin.AddFilter(new Gdk.FilterFunc(FilterFunction));
    }

    public event EventHandler Pressed;

    public void Register()
    {
        Gdk.Window rootWin = Gdk.Global.DefaultRootWindow;
        IntPtr xDisplay = GetXDisplay(rootWin);

        XGrabKey(
                 xDisplay,
                 this.keycode,
                 (uint)this.modifiers,
                 GetXWindow(rootWin),
                 false,
                 GrabModeAsync,
                 GrabModeAsync);     
    }

    public void Unregister()
    {
        Gdk.Window rootWin = Gdk.Global.DefaultRootWindow;
        IntPtr xDisplay = GetXDisplay(rootWin);

        XUngrabKey(
                 xDisplay,
                 this.keycode,
                 (uint)this.modifiers,
                 GetXWindow(rootWin));
    }

    private Gdk.FilterReturn FilterFunction(IntPtr xEvent, Gdk.Event evnt)
    {
        XKeyEvent xKeyEvent = (XKeyEvent)Marshal.PtrToStructure(
            xEvent, 
            typeof(XKeyEvent));

        if (xKeyEvent.type == KeyPress)
        {
            if (xKeyEvent.keycode == this.keycode 
                && xKeyEvent.state == (uint)this.modifiers)
            {
                this.OnPressed(EventArgs.Empty);
            }
        }

        return Gdk.FilterReturn.Continue;
    }

    protected virtual void OnPressed(EventArgs e)
    {
        EventHandler handler = this.Pressed;
        if (handler != null)
        {
            handler(this, e);
        }
    }

    private static IntPtr GetXWindow(Gdk.Window window)
    {
        return gdk_x11_drawable_get_xid(window.Handle);
    }

    private static IntPtr GetXDisplay(Gdk.Window window)
    {
        return gdk_x11_drawable_get_xdisplay(
            gdk_x11_window_get_drawable_impl(window.Handle));
    }

    [DllImport("libgtk-x11-2.0")]
    private static extern IntPtr gdk_x11_drawable_get_xid(IntPtr gdkWindow); 

    [DllImport("libgtk-x11-2.0")]
    private static extern IntPtr gdk_x11_drawable_get_xdisplay(IntPtr gdkDrawable);

    [DllImport("libgtk-x11-2.0")]
    private static extern IntPtr gdk_x11_window_get_drawable_impl(IntPtr gdkWindow);

    [DllImport("libX11")]
    private static extern int XKeysymToKeycode(IntPtr display, int key);

    [DllImport("libX11")]
    private static extern int XGrabKey(
        IntPtr display, 
        int keycode, 
        uint modifiers, 
        IntPtr grab_window, 
        bool owner_events, 
        int pointer_mode, 
        int keyboard_mode);

    [DllImport("libX11")]
    private static extern int XUngrabKey(
        IntPtr display, 
        int keycode, 
        uint modifiers, 
        IntPtr grab_window);

#if BUILD_FOR_32_BIT_X11        

    [StructLayout(LayoutKind.Sequential)]
    internal struct XKeyEvent
    {
        public short type;
        public uint serial;
        public short send_event;
        public IntPtr display;
        public uint window;
        public uint root;
        public uint subwindow;
        public uint time;
        public int x, y;
        public int x_root, y_root;
        public uint state;
        public uint keycode;
        public short same_screen;
    }       
#elif BUILD_FOR_64_BIT_X11

    [StructLayout(LayoutKind.Sequential)]
    internal struct XKeyEvent
    {
        public int type;
        public ulong serial;
        public int send_event;
        public IntPtr display;
        public ulong window;
        public ulong root;
        public ulong subwindow;
        public ulong time;
        public int x, y;
        public int x_root, y_root;
        public uint state;
        public uint keycode;
        public int same_screen;
    }
#endif      

}

And here is the test program:

public static void Main (string[] args)
{
    Application.Init();

    X11Hotkey hotkey = new X11Hotkey(Gdk.Key.A, Gdk.ModifierType.ControlMask);
    hotkey.Pressed += HotkeyPressed;;
    hotkey.Register();

    Application.Run();

    hotkey.Unregister();
}

private static void HotkeyPressed(object sender, EventArgs e)
{
    Console.WriteLine("Hotkey Pressed!");
}

I'm not sure how the XKeyEvent structure will behave on other systems with different sizes for C ints and longs, so whether this solution will work on all systems remains to be seen.

Edit: It looks like this solution is not going to be architecture-independent as I feared, due to the varying nature of the underlying C type sizes. libgtkhotkey looks promising as way to avoid deploying and compiling custom unmanaged libraries with your managed assemblies.

Note: Now you need to explicity define BUILD_FOR_32_BIT_X11 or BUILD_FOR_64_BIT_X11 depending on the word-size of your OS.

甲如呢乙后呢 2024-08-30 14:26:44

我是这个网站的新手,由于我的声誉不足,我似乎无法对之前的问题发表评论。 (抱歉,我什至不能给你投票!)

关于不同基础大小的问题,我认为这可以通过使用 IntPtr 来解决多头。这遵循 Mono 项目文档中的建议,请参阅 http://www.mono-project.com/ Interop_with_Native_Libraries#Longs。 C 类型 int 和 Bool 应映射到 C# int。

关于GAPI包装器,我尝试过,但无法让它工作。如果扎克可以发布任何有关他如何做到这一点的信息,我将不胜感激。

我也无法让示例程序运行。与 SDX2000 一样,我必须编辑库名称,并添加 using 语句。我在使用 Application.Init() 时遇到了问题,最终我将其换成了创建表单。但我的注册调用仍然因 BadRequest 而失败。如果任何已经完成此工作的人可以更新代码以使其更加完整,我将不胜感激。

I'm new to this site and it seems that I can't leave a comment on a previous question as I have insufficient reputation. (Sorry I can't even up-vote you!)

Relating to the issue of differing underlying sizes, I think this is solvable by using an IntPtr for the longs. This follows a suggestion in the Mono project documentation, see http://www.mono-project.com/Interop_with_Native_Libraries#Longs. The C types int and Bool should map to C# int.

Regarding the GAPI wrapper, I tried it, but couldn't get it working. If Zach could post any info on how he did it, I'd be grateful.

Also I couldn't get the sample program to work. Like SDX2000, I had to edit the library names, and I added using statements. I had a problem with Application.Init(), which in the end I swapped for creating a Form. But still my register call fails with BadRequest. If anyone who has got this working can update the code to make it more complete I'd be grateful.

超可爱的懒熊 2024-08-30 14:26:44

Tomboy 有一些代码知道如何做到这一点,我会从那里获取代码。

Tomboy has some code that knows how to do this, I'd take the code from there.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文