Skip to main content
 首页 » 编程设计

x11之X11/Xlib 的全局热键

2025年05月04日29jyk

我的目标是让一个程序在后台休眠,但可以由用户通过一些“热键”激活。通过深入研究 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; 
} 

请您参考如下方法:

您的程序在这里运行。我的猜测是您激活了另一个修饰符,例如 NumLock。 GrabKey 仅适用于精确修饰符掩码。

例如,这里有一些来自 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); 
}