使用 X11/Xlib 的全局热键
Posted
技术标签:
【中文标题】使用 X11/Xlib 的全局热键【英文标题】:Global Hotkey with X11/Xlib 【发布时间】:2011-05-01 12:51:21 【问题描述】:我的目标是让程序在后台休眠,但可以由用户通过一些“热键”激活。通过挖掘 Xlib 手册和 Xlib O'reilly 手册,我认为正确的方法是使用 XGrabKey。然而,我对该过程的理解是不正确的,因为简单的概念证明不起作用。
我的理解是,如果我用根窗口作为grab_window 调用XGrabKey,并且owner_events 为false,那么只要按下我的热键,事件就会仅发送到根窗口。如果我然后从根窗口中选择 KeyPress 事件,然后监听 X 事件,我应该在按下热键时得到一个按键事件。我在下面粘贴了一个最小的示例。
我期望的是,当程序运行时,无论哪个窗口有焦点,如果按下 Ctrl+Shift+K,我的程序应该输出“按下热键!”在控制台中,然后终止。
此外,我的理解是,如果 XGrabKey 失败,默认错误处理程序将显示一条消息,因此我假设调用成功。
显然,我的理解在某种程度上存在缺陷。谁能指出我正确的方向?
#include <iostream>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
using namespace std;
int main()
Display* dpy = XOpenDisplay(0);
Window root = DefaultRootWindow(dpy);
XEvent ev;
unsigned int modifiers = ControlMask | ShiftMask;
int keycode = XKeysymToKeycode(dpy,XK_Y);
Window grab_window = root;
Bool owner_events = False;
int pointer_mode = GrabModeAsync;
int keyboard_mode = GrabModeAsync;
XGrabKey(dpy, keycode, modifiers, grab_window, owner_events, pointer_mode,
keyboard_mode);
XSelectInput(dpy, root, KeyPressMask );
while(true)
bool shouldQuit = false;
XNextEvent(dpy, &ev);
switch(ev.type)
case KeyPress:
cout << "Hot key pressed!" << endl;
XUngrabKey(dpy,keycode,modifiers,grab_window);
shouldQuit = true;
default:
break;
if(shouldQuit)
break;
XCloseDisplay(dpy);
return 0;
【问题讨论】:
在你的代码中你使用XK_Y
,你可能想改成XK_K
?
【参考方案1】:
您的程序在这里工作。我的猜测是您有另一个激活的修饰符,例如 NumLock。 GrabKey 仅适用于 exact 修饰符掩码。
例如这里是 metacity 窗口管理器的一些 (GPL) 代码
/* Grab/ungrab, ignoring all annoying modifiers like NumLock etc. */
static void
meta_change_keygrab (MetaDisplay *display,
Window xwindow,
gboolean grab,
int keysym,
unsigned int keycode,
int modmask)
unsigned int ignored_mask;
/* Grab keycode/modmask, together with
* all combinations of ignored modifiers.
* X provides no better way to do this.
*/
meta_topic (META_DEBUG_KEYBINDINGS,
"%s keybinding %s keycode %d mask 0x%x on 0x%lx\n",
grab ? "Grabbing" : "Ungrabbing",
keysym_name (keysym), keycode,
modmask, xwindow);
/* efficiency, avoid so many XSync() */
meta_error_trap_push (display);
ignored_mask = 0;
while (ignored_mask <= display->ignored_modifier_mask)
if (ignored_mask & ~(display->ignored_modifier_mask))
/* Not a combination of ignored modifiers
* (it contains some non-ignored modifiers)
*/
++ignored_mask;
continue;
if (meta_is_debugging ())
meta_error_trap_push_with_return (display);
if (grab)
XGrabKey (display->xdisplay, keycode,
modmask | ignored_mask,
xwindow,
True,
GrabModeAsync, GrabModeSync);
else
XUngrabKey (display->xdisplay, keycode,
modmask | ignored_mask,
xwindow);
if (meta_is_debugging ())
int result;
result = meta_error_trap_pop_with_return (display, FALSE);
if (grab && result != Success)
if (result == BadAccess)
meta_warning (_("Some other program is already using the key %s with modifiers %x as a binding\n"), keysym_name (keysym), modmask | ignored_mask);
else
meta_topic (META_DEBUG_KEYBINDINGS,
"Failed to grab key %s with modifiers %x\n",
keysym_name (keysym), modmask | ignored_mask);
++ignored_mask;
meta_error_trap_pop (display, FALSE);
【讨论】:
天哪。你是绝对正确的。数字锁定已打开。非常感谢。我今天在愚蠢上浪费了几个小时,但我认为你让我免于浪费更多时间。【参考方案2】:使用您的掩码ControlMask | ShiftMask
,如果按住另一个修饰键,您将无法获得该键。一开始这听起来不错,但有一个陷阱:NumLock、CapsLock 等都被视为修饰符。
你有两个选择:
您多次致电XGrabKey()
,每次您感兴趣的明确组合一次。
您使用AnyModifier
调用XGrabKey()
并使用event.xkey.state
检查修饰符是否符合您的预期。
头文件<X.h>
定义了ShiftMask
、LockMask
、ControlMask
、Mod1Mask
、Mod2Mask
、Mod3Mask
、Mod4Mask
、Mod5Mask
和AnyModifier
。
关键是:
Mask | Value | Key
------------+-------+------------
ShiftMask | 1 | Shift
LockMask | 2 | Caps Lock
ControlMask | 4 | Ctrl
Mod1Mask | 8 | Alt
Mod2Mask | 16 | Num Lock
Mod3Mask | 32 | Scroll Lock
Mod4Mask | 64 | Windows
Mod5Mask | 128 | ???
警告我通过尝试发现了 ModNMask
键,但我不知道这是否适用于所有机器/配置/版本/操作系统。
在您的情况下,您可能希望确保设置了ShiftMask | CtrlMask
,Mod1Mask | Mod4Mask
是明确的,而其他的则被忽略。
我会这样做来设置密钥抓取:
XGrabKey(dpy, keycode, AnyModifier, grab_window, owner_events, pointer_mode, keyboard_mode);
检查是否设置了正确的修饰符:
switch (ev.type)
case KeyPress:
if ((ev.xkey.state & (ShiftMask | CtrlMask | Mod1Mask | Mod4Mask)) == (ShiftMask | CtrlMask))
// ...
【讨论】:
值128
对应于我系统上的ISO_Level3_Shift。【参考方案3】:
如果您在 X11 上使用/定位 gtk,则有一个界面更简单的 C 库:
https://github.com/engla/keybinder
包括 Python、Lua 和 Vala 绑定。 (也提到了here。)
【讨论】:
以上是关于使用 X11/Xlib 的全局热键的主要内容,如果未能解决你的问题,请参考以下文章