全局热键X11/Xlib

che*_*kow 28 x11 hotkeys xlib

我的目标是让一个程序在后台休眠,但可以由用户通过一些"热键"激活.从挖掘Xlib手册和Xlib O'reilly手册开始,我认为正确的方法是使用XGrabKey.但是我对这个过程的理解是不正确的,因为简单的概念证明不起作用.

我的理解是,如果我叫XGrabKey与根窗口作为grab_window和owner_events假,每当我的热键按下事件,然后将发送到根窗口.如果我然后从根窗口选择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;
}
Run Code Online (Sandbox Code Playgroud)

Hav*_*c P 21

你的程序在这里工作.我的猜测是你有另一个修饰符活跃,比如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);
}
Run Code Online (Sandbox Code Playgroud)

  • 天啊.你是绝对正确的.Num lock已开启.非常感谢.我今天愚蠢地浪费了几个小时,但我认为你让我免于浪费更多. (4认同)

Chr*_*jer 9

ControlMask | ShiftMask如果持有另一个修改键,使用掩码将无法获得密钥.这首先听起来不错,但是有一个陷阱:NumLock,CapsLock和所有类似的都被视为修饰符.

您有两种选择:

  • 您可以XGrabKey()多次调用一次,对于您感兴趣的每个显式组合.
  • 你叫XGrabKey()AnyModifier和使用event.xkey.state检查修饰符是否如你预期.

头文件<X.h>定义ShiftMask,LockMask,ControlMask,Mod1Mask,Mod2Mask,Mod3Mask,Mod4Mask,Mod5MaskAnyModifier.

关键是:

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 | ???
Run Code Online (Sandbox Code Playgroud)

警告ModNMask通过尝试找到了密钥,但我不知道这是否适用于所有机器/配置/版本/操作系统.

在您的情况下,您可能希望确保ShiftMask | CtrlMask设置,Mod1Mask | Mod4Mask清除,其他要忽略.

我这样做是为了设置关键抓取:

XGrabKey(dpy, keycode, AnyModifier, grab_window, owner_events, pointer_mode, keyboard_mode);
Run Code Online (Sandbox Code Playgroud)

这是为了检查是否设置了正确的修饰符:

switch (ev.type)  {
case KeyPress:
    if ((ev.xkey.state & (ShiftMask | CtrlMask | Mod1Mask | Mod4Mask)) == (ShiftMask | CtrlMask))
        // ...
}
Run Code Online (Sandbox Code Playgroud)

  • 值"128"对应于我系统上的__ISO_Level3_Shift__. (2认同)

Anu*_*ool 8

如果您在X11上使用/定位gtk,那么有一个C库,它具有更简单的接口:

https://github.com/engla/keybinder

包括Python,Lua和Vala绑定.(这里也提到.)