X11/Xlib 的全局热键

发布于 2024-09-29 05:28:37 字数 1677 浏览 1 评论 0原文

我的目标是拥有一个在后台休眠但可以由用户通过某些“热键”激活的程序。通过深入研究 Xlib 手册和 Xlib O'reilly 手册,我发现正确的方法是使用 XGrabKey。然而,我对这个过程的理解是不正确的,因为简单的概念证明不起作用。

我的理解是,如果我使用根窗口作为grab_window调用XGrabKey,并且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;
}

My goal is to have a program that sleeps in the background but can be activated by the user via some "hotkey". From digging around the Xlib manual and the Xlib O'reilly manual, I gather that the correct way to to this is with XGrabKey. However my understanding of the process is incorrect as a simple proof of concept does not work.

My understanding is that if I call XGrabKey with the root window as the grab_window, and owner_events false, then whenever my hotkey is pressed the event will be sent only to the root window. If I then select KeyPress events from the root window, and then listen for X events, I should get a key press event when the hotkey is pressed. I've pasted a minimal example below.

What I expect is that when the program is run, regardless of what window has focus, if Ctrl+Shift+K is pressed, my program should output "Hot key pressed!" in the console, and then terminate.

Furthermore, it is my understanding that if the XGrabKey fails, the default error handler will display a message, and since it does not I am assuming that the call succeeds.

Obviously, my understanding is flawed somehow. Can anyone point me in the right direction?

#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;
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

我只土不豪 2024-10-06 05:28:38

使用您的面罩 ControlMask | ShiftMask 如果按住另一个修饰键,您将无法获得该键。这听起来不错,但有一个陷阱:NumLockCapsLock 等都被视为修饰符。

您有两种选择:

  • 多次调用 XGrabKey(),针对您感兴趣的每个显式组合调用一次。
  • 您使用 AnyModifier< 调用 XGrabKey() /code> 并使用 event.xkey.state 检查修饰符是否符合您的预期。

头文件定义了ShiftMaskLockMaskControlMaskMod1MaskMod2MaskMod3MaskMod4MaskMod5MaskAnyModifier

这些键是:

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))
        // ...
}

With your mask ControlMask | ShiftMask you will not get the key if another modifier key is held. This sounds okay in the first place, but there's a pitfall: NumLock, CapsLock and alike all are treated as modifiers, too.

You have two options:

  • You call XGrabKey() multiple times, once for each explicit combination that you're interested in.
  • You call XGrabKey() with AnyModifier and use event.xkey.state to check whether the modifiers are as you expected.

The header file <X.h> defines ShiftMask, LockMask, ControlMask, Mod1Mask, Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask and AnyModifier.

The keys are:

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 | ???

Warning I found out about the ModNMask keys by trying and I do not know if this is valid on all machines / configurations / versions / operating systems.

In your case, you probably want to make sure that ShiftMask | CtrlMask is set, Mod1Mask | Mod4Mask are clear, and the others to be ignored.

I'd do this to setup the key grab:

XGrabKey(dpy, keycode, AnyModifier, grab_window, owner_events, pointer_mode, keyboard_mode);

And this to check whether the right modifiers are set:

switch (ev.type)  {
case KeyPress:
    if ((ev.xkey.state & (ShiftMask | CtrlMask | Mod1Mask | Mod4Mask)) == (ShiftMask | CtrlMask))
        // ...
}
沉睡月亮 2024-10-06 05:28:38

如果您在 X11 上使用/定位 gtk,则有一个具有更简单界面的 C 库:

https://github .com/engla/keybinder

包括 Python、Lua 和 Vala 绑定。 (此处也提到了。)

If you're using/targeting gtk on X11, there's a C library with a much simpler interface:

https://github.com/engla/keybinder

Includes Python, Lua and Vala bindings. (Also mentioned here.)

莫相离 2024-10-06 05:28:37

你的程序在这里运行。我的猜测是您有另一个处于活动状态的修饰符,例如 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);
}

Your program works here. My guess is you have another modifier active, such as NumLock. GrabKey only works on the exact modifier mask.

For example here is some (GPL) code from metacity window manager

/* 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);
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文