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!
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:
And here is the test program:
I'm not sure how the
XKeyEvent
structure will behave on other systems with different sizes for Cint
s andlong
s, 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
orBUILD_FOR_64_BIT_X11
depending on the word-size of your OS.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.
Tomboy has some code that knows how to do this, I'd take the code from there.