Global Hotkey in Mono and Gtk #

I am trying to get a global hotkey working on Linux using Mono. I found the signatures of XGrabKey and XUngrabKey , but I cannot get them to work. Whenever I try to call XGrabKey , the application crashes using 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 XGrabKey example?

Thanks!

+4
source share
3 answers

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.

+7
source

I am new to this site and it seems that I can not leave a comment on the previous question, because I do not have enough reputation. (Sorry, I can't even vote for you!)

Regarding the problem with different base sizes, I think this is solvable using IntPtr for longs. This follows the suggestion in the Mono project documentation, see http://www.mono-project.com/Interop_with_Native_Libraries#Longs . C types int and Bool should map to C # int.

As for the GAPI shell, I tried it, but could not get it to work. If Zack could post any information on how he did this, I would be grateful.

Also, I could not get the sample program to work. Like the SDX2000, I had to edit the library names, and I added the use of operators. I had a problem with Application.Init (), which eventually replaced me to create the Form. But still my register call is not being made using BadRequest. If someone who got this work can update the code to make it more complete, I would be grateful.

+2
source

Tomboy has a code that knows how to do this, I would take the code from there.

+1
source

Source: https://habr.com/ru/post/1302639/


All Articles