有没有人知道一个xlib函数来捕获一个按键事件而不会丢失原始焦点?如何摆脱它?
(或“使用XGrabKey()而不生成抓取式焦点”?)
(或“如何在系统级别摆脱NotifyGrab和NotifyUngrab焦点事件?”
XGrabKey将失去对关键按下和恢复焦点的关注。
我想捕获按键而不将其泄漏到原始窗口(正如XGrabKey可以做到的那样)。
参考文献:
...... XGrabKey会抢镜头...... https://bugs.launchpad.net/gtkhotkey/+bug/390552/comments/8
...程序接受控制以响应键组合做某事。同时,该计划暂时集中...... During XGrabKey(board), discover which window had been focused
... XGrabKeyboard函数主动抓取键盘控制并生成FocusIn和FocusOut事件...... http://www.x.org/archive/X11R6.8.0/doc/XGrabKeyboard.3.html#toc3
......我看不到了 提供metacity当前桌面改变行为的方式(改变 并且在不导致的情况下显示弹出对话框 抓住窗户的焦点... https://mail.gnome.org/archives/wm-spec-list/2007-May/msg00000.html
...全屏模式不应退出使用NotifyGrab的FocusOut事件... https://bugzilla.mozilla.org/show_bug.cgi?id=578265
抓住键盘不允许改变焦点...... grabbing keyboard doesnt allow changing focus
Grabs生成的焦点事件(XGrabKeyboard的主动抓取和XGrabKey的被动抓取) http://www.x.org/releases/X11R7.6/doc/libX11/specs/libX11/libX11.html#Focus_Events_Generated_by_Grabs
XGrabKey源代码:http://cgit.freedesktop.org/xorg/lib/libX11/tree/src/GrKey.c也许我们可以修改它来摆脱焦点事件?
有“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
答案 0 :(得分:12)
我在90年代早期看过Irix,ultrix和solaris的全球热键,因为它在我的Acorn BBC电脑上很容易做到。最终,我们决定以低于xlib的级别以非便携方式使用一些专有代码来解决这个问题。 由于我们的软件安装需要作为超级用户权限,我们能够插入适当的软件挂钩作为守护进程。
对于Linux(现在),您应该通过在操作系统级别处理键盘事件来寻找软件解决方案。我先来看看这里:http://code.google.com/p/logkeys/
更通用的解决方案是使用带有USB输入和USB输出的小型PC板,它可以作为鼠标和键盘对计算机起作用,并根据需要翻译键盘键。但如果你想经常改变映射,这就不会那么灵活了。
答案 1 :(得分:12)
我当前的代码(来自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”键时,焦点丢失,当我释放它时,焦点会回来。
答案 2 :(得分:5)
看起来XQueryKeymap会对你进行排序。请参阅下面的C++ source code I found:
/* 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);
}
注意,这不是经过测试的代码,也不是我的 - 我只是在互联网上找到它。
答案 3 :(得分:5)
最后,正如你所知道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=libgtk2.0-0_2.24.17-0ubuntu2_i386.deb
答案 4 :(得分:3)
我有一个想法,我很确定会工作,但我必须睡觉,不能自己测试,它不漂亮,因为我不认为有任何方法在X中做你想做的事。这是我想到的步骤。简而言之:在X中禁用键盘,从较低级别的api读取事件,并自己选择性地将它们提供给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下搜索名称(在xinput -list中给出)并以这种方式打开它。这可能在某些时候需要root,因为这些权限通常都是非常严格的限制。
5)从那里读取键盘输入:
输入事件的数据格式为:
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位,ushorts是16位。由于您只对键盘输入感兴趣,因此您可以非常简单地执行此操作:
读取并忽略8个字节。
下一个字节应为1,然后下一个字节为0.如果不是,请跳过此事件
下一个字节是密钥代码的小端,因为它是&lt; 255键,这是足够好的
跳过下一个字节。
读取下一个字节以查看是否按下或释放了
跳过接下来的三个字节
6)当你得到一个你想要捕获的事件时,请自己处理。否则,使用XSendEvent将其发送到X,以便正常处理。将从/ dev / input获得的硬件代码映射到适当的keysym可能是一个技巧,但我相当确定xlib中有一个函数来帮助解决这个问题。
7)转到5并循环直到你完成
8)确保将所有内容都设置为退出时的状态,或者您可以将用户的键盘输入分解为X!
我建议使用第二个USB键盘进行测试,你可以使用/ dev / input和xinput相互独立地禁用和监听键盘,所以如果你崩溃了,你仍然可以使用第一个键盘并正常工作。 (实际上,我认为用第二个键盘故意做到这一点非常酷,加倍热键!)
但是,是的,需要root并且可能让键盘“脱离”X并不是很漂亮,并且使用SendKey转发可能说起来容易做起来难,但我很确定这个会工作并为您提供最大的灵活性。
答案 5 :(得分:1)
对于编写一个键(快捷方式)映射软件,还要查看libtermkey
,一个终端键输入库(用C编写),它识别XTerm风格的鼠标位置/按钮报告,特殊键(如箭头)和功能键),包括Ctrl-Left
等“修改”键。
例如,POE::Wheel::TermKey
是一个围绕libtermkey
库的异步perl包装器,它提供了一种抽象方式来读取基于终端的程序中的按键事件。“
答案 6 :(得分:0)
您可以使用XQueryKeymap来阅读事件,然后您可以使用XTestKey发送退格键事件,然后使用您想要按下的键。更好的是,您可以register hotkeys获取所有键盘事件,然后使用XTestKey生成键事件。 BTW,KDE的“自定义快捷方式”控制模块允许使用快捷方式生成按键。 Source code