XGrabKey 不工作

XGrabKey not working

我目前正在为我的应用程序 AudioCuesheetEditor 进行媒体密钥识别。该应用程序使用单声道和 gtk#。所以我在 linux 上制作了直接访问 XGrabKey 的包装器(在 windows 上我使用了 SetWindowsHookEx)。但是现在过滤器函数没有被调用,我不知道,为什么?!

class代码:

public class HotkeyListener
    {
#if BUILD_FOR_WINDOWS
        private const int WH_KEYBOARD_LL = 13;
        private const int WM_KEYDOWN = 0x0100;

        public const int VK_MEDIA_PLAY_PAUSE = 0xB3;
        public const int VK_MEDIA_NEXT_TRACK = 0xB0;
        public const int VK_MEDIA_PREV_TRACK = 0xB1;
        public const int VK_MEDIA_STOP = 0xB2;

        private LowLevelKeyboardProc _proc;
        private static IntPtr _hookID = IntPtr.Zero;
#endif
#if BUILD_FOR_LINUX
        private const int KeyPress = 2;
        private const int GrabModeAsync = 1;

        public const int VK_MEDIA_PLAY_PAUSE = 172;
        public const int VK_MEDIA_NEXT_TRACK = 153;
        public const int VK_MEDIA_PREV_TRACK = 144;
        public const int VK_MEDIA_STOP = 164;
#endif
        private static readonly Logfile log = Logfile.getLogfile(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
        private int keyCode;

        public HotkeyListener(int vkCode)
        {
#if BUILD_FOR_WINDOWS
            _proc = HookCallback;
#endif
            this.keyCode = vkCode;
        }

        public event HotKeyEvent HotkeyPressed;

        public void Register() 
        {
#if BUILD_FOR_WINDOWS
            _hookID = SetHook(_proc);
#endif
#if BUILD_FOR_LINUX
            Gdk.Window rootWin = Gdk.Global.DefaultRootWindow;
            IntPtr xDisplay = GetXDisplay(rootWin);
            XGrabKey(xDisplay,this.keyCode,(uint)Gdk.ModifierType.ModifierMask,GetXWindow(rootWin),false,GrabModeAsync,GrabModeAsync);
#endif
        }

        public void Unregister()
        {
#if BUILD_FOR_WINDOWS
            UnhookWindowsHookEx(_hookID);
#endif
#if BUILD_FOR_LINUX
            Gdk.Window rootWin = Gdk.Global.DefaultRootWindow;
            IntPtr xDisplay = GetXDisplay(rootWin);    
            XUngrabKey(xDisplay,this.keyCode,(uint)Gdk.ModifierType.ModifierMask,GetXWindow(rootWin));
#endif
        }

        public delegate void HotKeyEvent();

#if BUILD_FOR_WINDOWS

        private static IntPtr SetHook(LowLevelKeyboardProc proc)
        {
            using (Process curProcess = Process.GetCurrentProcess())
            using (ProcessModule curModule = curProcess.MainModule)
            {
                return SetWindowsHookEx(WH_KEYBOARD_LL, proc, GetModuleHandle(curModule.ModuleName), 0);
            }
        }

        private IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
        {
            if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN)
            {
                int vkCode = Marshal.ReadInt32(lParam);
                log.debug("vk = " + vkCode);
                if ((this.HotkeyPressed != null) && (this.keyCode == vkCode))
                {
                    this.HotkeyPressed();
                }
            }
            return CallNextHookEx(_hookID, nCode, wParam, lParam);
        }

        private delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr SetWindowsHookEx(int idHook, LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool UnhookWindowsHookEx(IntPtr hhk);

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode,IntPtr wParam, IntPtr lParam);

        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr GetModuleHandle(string lpModuleName);

#endif

#if BUILD_FOR_LINUX

        private Gdk.FilterReturn FilterFunction(IntPtr xEvent, Gdk.Event evnt)
        {
            XKeyEvent xKeyEvent = (XKeyEvent)Marshal.PtrToStructure(xEvent, typeof(XKeyEvent));

            if (xKeyEvent.type == KeyPress)
            {
                log.debug("vk = " + xKeyEvent.keycode);
                if (xKeyEvent.keycode == this.keyCode)
                {
                    this.HotkeyPressed();
                }
            }

            return Gdk.FilterReturn.Continue;
        }

        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 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);

        [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
    }

我在FilterFunction里面下了断点,但是系统没有断:(.

热键监听器的用法:

this.hotkeyMediaPlayListener = new HotkeyListener(HotkeyListener.VK_MEDIA_PLAY_PAUSE);
this.hotkeyMediaPlayListener.HotkeyPressed += delegate {
                if (this.objProgram.getObjOption().getBMediakeyboardHotkeyRecognition() == true) 
                {
                    if (this.objProgram.getAudioManager().PlayStatus != AudioCuesheetEditor.AudioBackend.PlayState.Playing)
                    {
                        this.objProgram.getAudioManager().play();
                    }
                    else
                    {
                        this.objProgram.getAudioManager().pause();
                    }
                }
            };
            this.hotkeyMediaPlayListener.Register();

完整的项目代码可以在这里查看:http://sourceforge.net/p/audiocuesheet/code/HEAD/tree/

感谢大家的帮助。

我自己解决了问题,我没有在构造函数中将过滤器函数附加到根 window。这是代码,适用于所有人:

public class HotkeyListener
    {
#if BUILD_FOR_WINDOWS
        private const int WH_KEYBOARD_LL = 13;
        private const int WM_KEYDOWN = 0x0100;

        public const int VK_MEDIA_PLAY_PAUSE = 0xB3;
        public const int VK_MEDIA_NEXT_TRACK = 0xB0;
        public const int VK_MEDIA_PREV_TRACK = 0xB1;
        public const int VK_MEDIA_STOP = 0xB2;

        private LowLevelKeyboardProc _proc;
        private static IntPtr _hookID = IntPtr.Zero;
#endif
#if BUILD_FOR_LINUX
        private const int KeyPress = 2;
        private const int GrabModeAsync = 1;

        public const int VK_MEDIA_PLAY_PAUSE = 172;
        public const int VK_MEDIA_NEXT_TRACK = 153;
        public const int VK_MEDIA_PREV_TRACK = 144;
        public const int VK_MEDIA_STOP = 164;
#endif
        private static readonly Logfile log = Logfile.getLogfile(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
        private int keyCode;

        public HotkeyListener(int vkCode)
        {
#if BUILD_FOR_WINDOWS
            _proc = HookCallback;
#endif
            Gdk.Window rootWin = Gdk.Global.DefaultRootWindow;
            this.keyCode = vkCode;
            rootWin.AddFilter(new Gdk.FilterFunc(FilterFunction));
        }

        public event HotKeyEvent HotkeyPressed;

        public void Register() 
        {
#if BUILD_FOR_WINDOWS
            _hookID = SetHook(_proc);
#endif
#if BUILD_FOR_LINUX
            Gdk.Window rootWin = Gdk.Global.DefaultRootWindow;
            IntPtr xDisplay = GetXDisplay(rootWin);
            foreach(Gdk.ModifierType mod in Enum.GetValues(typeof(Gdk.ModifierType)))
            {
                XGrabKey(xDisplay,this.keyCode,(uint)mod,GetXWindow(rootWin),false,GrabModeAsync,GrabModeAsync);
            }
#endif
        }

        public void Unregister()
        {
#if BUILD_FOR_WINDOWS
            UnhookWindowsHookEx(_hookID);
#endif
#if BUILD_FOR_LINUX
            Gdk.Window rootWin = Gdk.Global.DefaultRootWindow;
            IntPtr xDisplay = GetXDisplay(rootWin);  
            foreach(Gdk.ModifierType mod in Enum.GetValues(typeof(Gdk.ModifierType)))
            {
                XUngrabKey(xDisplay,this.keyCode,(uint)mod,GetXWindow(rootWin));
            }
#endif
        }

        public delegate void HotKeyEvent();

#if BUILD_FOR_WINDOWS

        private static IntPtr SetHook(LowLevelKeyboardProc proc)
        {
            using (Process curProcess = Process.GetCurrentProcess())
            using (ProcessModule curModule = curProcess.MainModule)
            {
                return SetWindowsHookEx(WH_KEYBOARD_LL, proc, GetModuleHandle(curModule.ModuleName), 0);
            }
        }

        private IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
        {
            if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN)
            {
                int vkCode = Marshal.ReadInt32(lParam);
                log.debug("vk = " + vkCode);
                if ((this.HotkeyPressed != null) && (this.keyCode == vkCode))
                {
                    this.HotkeyPressed();
                }
            }
            return CallNextHookEx(_hookID, nCode, wParam, lParam);
        }

        private delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr SetWindowsHookEx(int idHook, LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool UnhookWindowsHookEx(IntPtr hhk);

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode,IntPtr wParam, IntPtr lParam);

        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr GetModuleHandle(string lpModuleName);

#endif

#if BUILD_FOR_LINUX

        private Gdk.FilterReturn FilterFunction(IntPtr xEvent, Gdk.Event evnt)
        {
            XKeyEvent xKeyEvent = (XKeyEvent)Marshal.PtrToStructure(xEvent, typeof(XKeyEvent));

            if (xKeyEvent.type == KeyPress)
            {
                log.debug("vk = " + xKeyEvent.keycode);
                if ((this.HotkeyPressed != null) && (this.keyCode == xKeyEvent.keycode))
                {
                    if (this.HotkeyPressed != null) 
                    this.HotkeyPressed();
                }
            }

            return Gdk.FilterReturn.Continue;
        }

        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 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);

        [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
    }