[英]Global Hotkey in Mono and Gtk#
我正在嘗試使用Mono在Linux中使用全局熱鍵。 我找到了XGrabKey
和XUngrabKey
的簽名,但我似乎無法讓它們正常工作。 每當我嘗試調用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 int
和long
s的不同大小的其他系統上的表現如何,所以這個解決方案是否適用於所有系統還有待觀察。
編輯:由於底層C類型大小的不同性質,看起來這個解決方案不會像我擔心的那樣獨立於架構。 libgtkhotkey看起來很有希望避免使用托管程序集部署和編譯自定義非托管庫。
注意:現在需要明確定義BUILD_FOR_32_BIT_X11
或BUILD_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有一些知道如何執行此操作的代碼,我會從那里獲取代碼。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.