Well, finally, I found a working solution in managed code. SIGSEGV was due to the fact that I misled the handles of unmanaged Gdk objects using the handles of their X11 counterparts. Thanks to Paul’s answer, I was able to find an uncontrollable example of global hotkeys and get acquainted with how it works. Then I wrote my own unmanaged test program to find out what I need to do without dealing with any controllable features. After that, I created a managed solution.
Here is a manageable 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 int and long s, so whether this solution remains working on all systems.
Edit: It seems that this solution will not be architecture independent, as I was afraid, due to the different nature of the base sizes of type C. libgtkhotkey looks promising as a way to avoid deploying and compiling custom unmanaged libraries using managed assemblies.
Note. . Now you need to define the exclusion BUILD_FOR_32_BIT_X11 or BUILD_FOR_64_BIT_X11 depending on the size of the word of your OS.