Mono和Gtk的全球热键#

我正在尝试使用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的工作示例?

谢谢!

好吧,我终于找到了托管代码中的工作解决方案。 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 intlong s的不同大小的其他系统上的表现如何,所以这个解决方案是否适用于所有系统还有待观察。

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

注意:现在您需要明确定义BUILD_FOR_32_BIT_X11BUILD_FOR_64_BIT_X11具体取决于操作系统的字大小。

我是这个网站的新手,似乎我不能对之前的问题发表评论,因为我的声誉不足。 (对不起,我甚至无法向你投票!)

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

关于GAPI包装器,我尝试过,但无法使其正常工作。 如果Zach可以发布关于他是如何做到这一点的任何信息,我将不胜感激。

此外,我无法让示例程序工作。 像SDX2000一样,我不得不编辑库名,我添加了使用语句。 我遇到了Application.Init()的问题,最后我交换了创建一个Form。 但仍然我的注册调用失败了BadRequest。 如果任何有此工作的人都可以更新代码以使其更完整,我将不胜感激。

Tomboy有一些知道如何执行此操作的代码,我会从那里获取代码。