web-dev-qa-db-ja.com

X11 / Xlibを使用したグローバルホットキー

私の目標は、バックグラウンドでスリープするが、ユーザーが「ホットキー」を介してアクティブ化できるプログラムを作成することです。 XlibマニュアルとXlibO'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;
}
29
cheshirekow

あなたのプログラムはここで機能します。私の推測では、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);
}
21
Havoc P

マスク_ControlMask | ShiftMask_を使用すると、別の修飾キーが保持されている場合、キーを取得できません。そもそもこれは問題ないように聞こえますが、落とし穴があります:NumLockCapsLockおよび同様に、すべても修飾子として扱われます。

2つのオプションがあります。

  • XGrabKey()を複数回呼び出します。関心のある明示的な組み合わせごとに、1回ずつ呼び出します。
  • AnyModifierを指定してXGrabKey()を呼び出し、_event.xkey.state_を使用して、修飾子が期待どおりかどうかを確認します。

ヘッダーファイル_<X.h>_は、ShiftMaskLockMaskControlMask、_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))
        // ...
}
_
9
Christian Hujer

X11でgtkを使用/ターゲティングしている場合は、はるかに単純なインターフェイスを備えたCライブラリがあります。

https://github.com/engla/keybinder

Python、Lua、Valaのバインディングが含まれています。 (また言及 ここ 。)

8
Anukool