为什么XGrabKey生成额外的焦点和焦点事件?

有没有人知道一个xlib函数来捕获按键事件,而不会失去原来的焦点? 如何摆脱它?

(或者“使用XGrabKey()而不生成抓取式聚焦”?)

(或者“在系统级别如何摆脱NotifyGrab和NotifyUngrab焦点事件?

XGrabKey将失去对按键的焦点,并恢复焦点释放的按键。

我想捕捉按键没有泄漏到原来的窗口(就像XGrabKey可以做到这一点)。

参考文献:

  1. … XGrabKey将窃取焦点… https://bugs.launchpad.net/gtkhotkey/+bug/390552/comments/8

  2. …程序收到控制权以响应组合键。 同时,程序已经暂时集中在 … 在XGrabKey(板),发现哪个窗口已经集中

  3. … XGrabKeyboard函数主动抓取键盘控件,并生成FocusIn和FocusOut事件… http://www.x.org/archive/X11R6.8.0/doc/XGrabKeyboard.3.html#toc3

  4. …我看不到一种方式来提供metacity当前的桌面changin行为(同时更改并显示popup对话框),而不会导致窗口上的Grabtypes焦点… https://mail.gnome .ORG /档案馆/ WM说明列表/ 2007-月/ msg00000.html

  5. …全屏模式不应该退出与NotifyGrab的FocusOut事件… https://bugzilla.mozilla.org/show_bug.cgi?id=578265

  6. 抓住键盘不允许改变焦点… 抓住键盘不允许改变焦点

  7. Grabs生成的焦点事件(XGrabKeyboard的主动抓取和XGrabKey的被动抓取) http://www.x.org/releases/X11R7.6/doc/libX11/specs/libX11/libX11.html#Focus_Events_Generated_by_Grabs

  8. XGrabKey源代码: http ://cgit.freedesktop.org/xorg/lib/libX11/tree/src/GrKey.c也许我们可以修改这个来摆脱焦点事件?

  9. 有“DoFocusEvents(keybd,oldWin,grab-> window,NotifyGrab);” 在ActivateKeyboardGrab()中: http : //cgit.freedesktop.org/xorg/xserver/tree/dix/events.c

我正在为按键组合(和鼠标移动)绘图软件写一个按键: https : //code.google.com/p/diyism-myboard/

我已经在Windows中用RegisterHotKey()和UnRegisterHotKey()实现了它: https : //code.google.com/p/diyism-myboard/downloads/detail? name = MyBoard.pas

我想用XGrabKey()和XUngrabKey()把它迁移到Linux中: https : //code.google.com/p/diyism-myboard/downloads/detail? name = myboard.py

我已经创造了10美元的赏金来解决这个问题。 我们需要更多的支持者发放奖金。 https://www.bountysource.com/issues/1072081-right-button-menu-flashes-while-jkli-keys-move-the-mouse-pointer

在90年代初期,我看到了Irix,ultrix和solaris的全球热键,因为在我的Acorn BBC电脑上很容易做到这一点。 最终我们决定在xlib以下的级别上使用一些专有代码以不可移植的方式解决这个问题。 由于我们的软件安装需要作为超级用户的特权,所以我们能够插入适当的软件钩子作为守护进程。

对于Linux(现在),您应该可以通过在os级别处理键盘事件来寻找软件解决scheme。 我会先看看这里: http : //code.google.com/p/logkeys/

一个更通用的解决scheme是使用一个带有USBinput和USB输出的小型PC板,该板将电脑当作鼠标和键盘,并根据需要转换键盘按键。 但是如果你想经常改变映射的话,这样做不会那么灵活。

我目前的代码(来自http://diyism-myboard.googlecode.com/files/myboard.py ):

disp=Display() screen=disp.screen() root=screen.root def grab_key(key, mod): key_code=string_to_keycode(key) #3rd: bool owner_events, 4th: pointer_mode, 5th: keyboard_mode, X.GrabModeSync, X.GrabModeAsync root.grab_key(key_code, mod, 0, X.GrabModeAsync, X.GrabModeAsync) root.grab_key(key_code, mod|X.LockMask, 0, X.GrabModeAsync, X.GrabModeAsync) #caps lock root.grab_key(key_code, mod|X.Mod2Mask, 0, X.GrabModeAsync, X.GrabModeAsync) #num lock root.grab_key(key_code, mod|X.LockMask|X.Mod2Mask, 0, X.GrabModeAsync, X.GrabModeAsync) def main(): grab_key('Shift_L', X.NONE) grab_key('Shift_R', X.NONE) while 1: evt=root.display.next_event() if evt.type in [X.KeyPress, X.KeyRelease]: #ignore X.MappingNotify(=34) handle_event(evt) if __name__ == '__main__': main() 

当我按下“shift”键,焦点丢失,当我释放它,焦点回来。

看起来像XQueryKeymap会sorting你。 看到下面我发现的C ++源代码 :

 /* compile with g++ keytest.cpp -LX11 -o keytest */ #include <X11/Xlib.h> #include <X11/Xutil.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/time.h> double gettime() { timeval tim; gettimeofday(&tim, NULL); double t1=tim.tv_sec+(tim.tv_usec/1000000.0); return t1; } int main() { Display *display_name; int depth,screen,connection; display_name = XOpenDisplay(NULL); screen = DefaultScreen(display_name); depth = DefaultDepth(display_name,screen); connection = ConnectionNumber(display_name); printf("Keylogger started\n\nInfo about X11 connection:\n"); printf(" The display is::%s\n",XDisplayName((char*)display_name)); printf(" Width::%d\tHeight::%d\n", DisplayWidth(display_name,screen), DisplayHeight(display_name,screen)); printf(" Connection number is %d\n",connection); if(depth == 1) printf(" You live in prehistoric times\n"); else printf(" You've got a coloured monitor with depth of %d\n",depth); printf("\n\nLogging started.\n\n"); char keys_return[32]; while(1) { XQueryKeymap(display_name,keys_return); for (int i=0; i<32; i++) { if (keys_return[i] != 0) { int pos = 0; int num = keys_return[i]; printf("%.20f: ",gettime()); while (pos < 8) { if ((num & 0x01) == 1) { printf("%d ",i*8+pos); } pos++; num /= 2; } printf("\n"); } } usleep(30000); } XCloseDisplay(display_name); } 

请注意,这不是testing代码,也不是我的 – 我只是在互联网上find它。

最后,正如你所知道的linux意味着自由,我修改了xserver以摆脱抢风格的焦点:

 sudo apt-get build-dep xorg-server apt-get source xorg-server cd xorg-server-* #modify or patch dix/events.c: comment off "DoFocusEvents(keybd, oldWin, grab->window, NotifyGrab);" in ActivateKeyboardGrab(), comment off "DoFocusEvents(keybd, grab->window, focusWin, NotifyUngrab);" in DeactivateKeyboardGrab() sudo apt-get install devscripts debuild -us -uc #"-us -uc" to avoid the signature step cd .. sudo dpkg --install xserver-xorg-core_*.deb #clear dependencies: sudo apt-mark auto $(apt-cache showsrc xorg-server | grep Build-Depends | perl -p -e 's/(?:[\[(].+?[\])]|Build-Depends:|,|\|)//g') sudo apt-get autoremove 

而且我还需要在gtk上下文菜单中删除XGrabKeyboard:

 sudo apt-get build-dep gtk+2.0 apt-get source gtk+2.0 cd gtk+2.0-* #modify or patch it: add "return TRUE;" in first line of popup_grab_on_window() of gtk/gtkmenu.c dpkg-source --commit debuild -us -uc #"-us -uc" to avoid the signature step, maybe need: sudo apt-get install devscripts cd .. sudo dpkg --install libgtk2.0-0_*.deb #clear dependencies: sudo apt-mark auto $(apt-cache showsrc gtk+2.0 | grep Build-Depends | perl -p -e 's/(?:[\[(].+?[\])]|Build-Depends:|,|\|)//g') sudo apt-get autoremove 

现在myboard.py运行良好。

如果你使用的是Ubuntu的raring-updates版本,你可以尝试一下:

https://code.google.com/p/diyism-myboard/downloads/detail?name=xserver-xorg-core_1.13.3-0ubuntu6.2_i386.deb

和:

https://code.google.com/p/diyism-myboard/downloads/detail?name=libgtk2.0-0_2.24.17-0ubuntu2_i386.deb

我有一个想法,我敢肯定会工作,但我必须上床,不能自己testing,这不是很好,因为我不认为有什么办法可以做你想在X.这是我想到的步骤。 简而言之:禁用X中的键盘,从较低级别的API读取事件,并select性地将它们自己送入X. 你必须在X中禁用键盘,否则,你可以看看事件,但不能阻止它; 你会和X一起阅读,而不是拦截它。

所以在这里它被打破了:

1)运行xinput -list得到键盘X正在使用

2)运行xinput list-props id来查找Device Enabled属性

3)运行xinput set-prop id prop 0来禁用X中的设备:

 xinput -list xinput list-props 12 # 12 is the id of the keyboard in the list... (example # btw) xinput set-prop 12 119 0 # 119 is the "Device Enabled" prop, we turn it off 

我不知道xinput是如何在xlib层面上工作的,我只是简单地把它叫做shell。

4)打开/dev/input/eventX ,其中X是键盘设备。 我实际上是在/ dev / input / by-id下search名字(在xinput -list中给出的),然后以这种方式打开它。 这可能需要根在某些时候,因为这些权限通常是相当严格的。

5)从那里读取键盘input:

input事件的数据格式是:

 struct input_event { int tv_sec; // time of the event int tv_usec; // ditto ushort type; // == 1 for key event ushort code; // key code, not the same as X keysyms, you should check it experimentally. 42 is left shift on mine, 54 is right shift int value; // for keys, 1 == pressed, 0 == released, 2 == repeat } 

整数是32位,ushort是16位。 既然你只是对键盘input感兴趣,你可以很简单地做到这一点:

  • 读取并忽略8个字节。

  • 下一个字节应该是1,那么下一个字节是0.如果不是,则跳过这个事件

  • 下一个字节是关键代码的小端,而且由于有<255个关键字,所以这样就足够了

  • 跳过下一个字节。

  • 读下一个字节,看它是否被按下或释放

  • 跳过接下来的三个字节

6)当你得到一个事件,你有兴趣陷阱,自己处理。 否则,使用XSendEvent将其发送给X,以便可以正常处理。 将从/ dev / input获得的硬件代码映射到相应的keysym可能是一个窍门,但是我相当确定xlib中有一个函数可以帮助解决这个问题。

7)转到5,然后循环,直到完成

8)确保你把所有的东西都放回原来的样子,或者你可以把用户的键盘input分解为X!

我build议使用第二个USB键盘来testing这个function,你可以用/ dev / input和xinput来禁用和监听相互独立的键盘,所以如果你崩溃的话,你还是有第一个键盘正常工作。 (实际上,我认为用第二个键盘故意做这个事情是非常酷的,双倍的热键!)

但是,需要root并且可能将键盘从X中“分离”出来并不是什么好事,用SendKey进行转发可能说起来容易做起来难,但我敢肯定这起作用,并给你最大的灵活性。

为了编写密钥(快捷方式)映射软件,还可以看看libtermkey ,用于识别XTerm式鼠标位置/button报告的terminal密钥input库(用C编写),特殊密钥(如箭头和function键),包括“修改“键如Ctrl-Left

例如, POE::Wheel::TermKey是“ libtermkey库周围的一个asynchronousperl包装器,它提供了一个抽象的方式来读取基于terminal的程序中的按键事件。

您可以使用XQueryKeymap来读取事件,然后您可以使用XTestKey发送退格键事件,然后使用您想要按下的键。 更好的是,您可以为所有键盘事件注册热键 ,然后使用XTestKey生成关键事件。 顺便说一句,KDE的“自定义快捷键”控制模块允许使用快捷键来生成按键。 源代码 。