2010-10-27 24 views
24

Mục tiêu của tôi là có một chương trình ngủ trong nền nhưng có thể được kích hoạt bởi người dùng thông qua một số "phím nóng". Từ việc đào bới hướng dẫn Xlib và sách hướng dẫn sử dụng Xlib O'reilly, tôi thu thập rằng cách chính xác để thực hiện điều này là với XGrabKey. Tuy nhiên, sự hiểu biết của tôi về quá trình này là không chính xác như một bằng chứng khái niệm đơn giản không hoạt động.Hotkey toàn cầu với X11/Xlib

Sự hiểu biết của tôi là nếu tôi gọi XGrabKey với cửa sổ gốc là grab_window và owner_events sai, thì bất cứ khi nào phím nóng của tôi được nhấn, sự kiện sẽ được gửi chỉ tới cửa sổ gốc. Nếu sau đó tôi chọn các sự kiện KeyPress từ cửa sổ gốc, và sau đó lắng nghe các sự kiện X, tôi sẽ nhận được một sự kiện nhấn phím khi phím nóng được nhấn. Tôi đã dán một ví dụ tối thiểu bên dưới. Điều tôi mong đợi là khi chương trình được chạy, bất kể cửa sổ nào có tiêu điểm, nếu nhấn Ctrl + Shift + K, chương trình của tôi sẽ xuất ra "Phím nóng được nhấn!" trong bảng điều khiển, và sau đó chấm dứt.

Hơn nữa, đó là sự hiểu biết của tôi rằng nếu XGrabKey không thành công, trình xử lý lỗi mặc định sẽ hiển thị thông báo và vì tôi không giả định rằng cuộc gọi thành công.

Rõ ràng, sự hiểu biết của tôi là thiếu sót bằng cách nào đó. ai đó có thể chỉ cho tôi phương hướng đúng không?

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

Trong mã của bạn, bạn sử dụng 'XK_Y', bạn có thể muốn nói 'XK_K' thay thế? –

Trả lời

19

Chương trình của bạn hoạt động tại đây. Đoán của tôi là bạn có một công cụ sửa đổi khác đang hoạt động, chẳng hạn như NumLock. GrabKey chỉ hoạt động trên chính xác mặt nạ sửa đổi.

Ví dụ ở đây là một số (GPL) mã từ quản lý cửa sổ metacity

/* 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); 
} 
+4

Oh man. Bạn hoàn toàn đúng. Num lock đã được bật. Cám ơn rất nhiều. Tôi đã lãng phí một vài giờ vào sự ngu ngốc của tôi ngày hôm nay, nhưng tôi nghĩ rằng bạn đã cứu tôi khỏi lãng phí nhiều hơn nữa. – cheshirekow

8

Nếu bạn đang sử dụng/nhắm mục tiêu gtk trên X11, có một thư viện C với một giao diện đơn giản hơn nhiều:

https://github.com/engla/keybinder

Bao gồm các ràng buộc Python, Lua và Vala. (Cũng được đề cập here.)

+1

một thư viện tốt. 1 cho bạn. Cảm ơn :-) – madper

7

Với mặt nạ ControlMask | ShiftMask bạn sẽ không nhận được chìa khóa nếu một phím bổ trợ khác được giữ. Điều này nghe có vẻ ổn ở nơi đầu tiên, nhưng có một lỗ hổng: NumLock, CapsLock và tất cả đều được coi là công cụ sửa đổi.

Bạn có hai lựa chọn:.

  • Bạn gọi XGrabKey() nhiều lần, một lần cho mỗi sự kết hợp rõ ràng rằng bạn quan tâm đến
  • Bạn gọi XGrabKey() với AnyModifier và sử dụng event.xkey.state để kiểm tra xem các bổ là như bạn mong đợi.

File header <X.h> định nghĩa ShiftMask, LockMask, ControlMask, Mod1Mask, Mod2Mask, Mod3Mask, Mod4Mask, Mod5MaskAnyModifier.

Các phím là:

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 tôi phát hiện ra về ModNMask phím bằng cách cố gắng và tôi không biết nếu điều này là hợp lệ trên tất cả các máy/cấu hình/phiên bản/hệ điều hành.

Trong trường hợp của bạn, bạn có thể muốn đảm bảo rằng ShiftMask | CtrlMask được đặt, Mod1Mask | Mod4Mask rõ ràng và các mục khác bị bỏ qua.

Tôi muốn làm điều này để thiết lập lấy chìa khóa:

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

Và điều này để kiểm tra xem các bổ đúng được thiết lập:

switch (ev.type) { 
case KeyPress: 
    if ((ev.xkey.state & (ShiftMask | CtrlMask | Mod1Mask | Mod4Mask)) == (ShiftMask | CtrlMask)) 
     // ... 
} 
+2

Giá trị '128' tương ứng với __ISO_Level3_Shift__ trên hệ thống của tôi. –