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

wsxa1bj1  于 2023-03-17  发布在  Linux
关注(0)|答案(8)|浏览(329)

有谁知道一个xlib函数可以捕获按键事件而不失去原来的焦点吗?如何摆脱它?
(or“使用XGrabKey()而不生成抓取式焦点”?)
(or“如何在系统级消除NotifyGrab和NotifyUngrab焦点事件?”
XGrabKey将在按下键时失去焦点,并在释放键时恢复焦点。
而且我想在不泄漏到原始窗口的情况下捕获按键(就像XGrabKey可以做到的那样)。
参考文献:
1.... XGrabKey将窃取焦点... https://bugs.launchpad.net/gtkhotkey/+bug/390552/comments/8
1....程序接收控制以响应组合键执行某项操作。同时,程序已暂时聚焦... During XGrabKey(board), discover which window had been focused

  1. XGrabKeyboard函数主动获取键盘控制权并生成FocusIn和FocusOut事件。http://www.x.org/archive/X11R6.8.0/doc/XGrabKeyboard.3.html#toc3
    1....我看不出有什么方法可以提供metacity当前的桌面更改行为(同时更改和显示弹出对话框),而不会导致窗口上出现抓取式焦点... https://mail.gnome.org/archives/wm-spec-list/2007-May/msg00000.html
    1.... FocusOut事件发生时,全屏模式不应退出,同时显示NotifyGrab... https://bugzilla.mozilla.org/show_bug.cgi?id=578265
    1.抓取键盘不允许更改焦点... grabbing keyboard doesnt allow changing focus
    1.抓取生成的焦点事件(XGrabKeyboard的主动抓取和XGrabKey的被动抓取)http://www.x.org/releases/X11R7.6/doc/libX11/specs/libX11/libX11.html#Focus_Events_Generated_by_Grabs
  2. XGrabKey源代码:也许我们可以修改它来消除焦点偏离事件?
    1.有“DoFocusEvents(按键、老Win、抓取-〉窗口、通知抓取);“在激活键盘抓取()中:http://cgit.freedesktop.org/xorg/xserver/tree/dix/events.c
    我正在写一个一键组合键(和鼠标移动)Map软件: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
hlswsv35

hlswsv351#

我的当前代码(来自https://github.com/diyism/MyBoard/blob/master/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”键时,焦点丢失,当我松开它时,焦点又回来了。

gkl3eglg

gkl3eglg2#

早在90年代初,我就研究过Irix、ultrix和solaris的全局热键,因为在我的Acorn BBC电脑上很容易做到。最终我们决定在xlib以下的级别上用一些专有代码以不可移植的方式解决这个问题。因为我们的软件安装需要超级用户权限,所以我们能够插入适当的软件钩子作为守护程序。
对于Linux(现在),你可能应该通过在操作系统级别处理键盘事件来寻找软件解决方案。我将从这里开始:http://code.google.com/p/logkeys/
一个更通用的解决方案是使用一个带有USB输入和USB输出的小型PC板,它可以充当鼠标和键盘,并根据需要转换键盘按键,但如果您想经常更改Map,则这种方法不太灵活。

pjngdqdw

pjngdqdw3#

最后,正如您所知,linux意味着自由,我修改了xserver以摆脱抓取式focusout:

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运行良好。
我已经把它从googlecode迁移到github:https://github.com/diyism/MyBoard

ocebsuys

ocebsuys4#

看起来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);
}

注意,这不是经过测试的代码,也不是我的--我只是在Internet上找到它。

vnjpjtjt

vnjpjtjt5#

我有一个想法,我很确定它会起作用,但我必须上床睡觉,不能亲自测试它,而且它并不漂亮,因为我不认为有任何方法可以在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
}

int是32位的,uscharges是16位的,因为你只对键盘输入感兴趣,你可以很简单地做到这一点:

  • 读取并忽略8个字节。
  • 下一个字节应该是1,然后下一个字节是0。如果不是,请跳过此事件
  • 下一个字节是密钥代码的小结尾,由于小于255个密钥,这就足够了
  • 跳过下一个字节。
  • 读取下一个字节,看它是按下还是释放
  • 跳过接下来的三个字节

6)当你得到一个你想捕获的事件时,你自己处理它。否则,使用XSendEvent把它发送到X,这样它就可以正常处理了。把你从/dev/input得到的硬件代码Map到适当的keysym可能是一个技巧,但是我相当肯定在xlib的某个地方有一个函数可以帮助你做到这一点。
7)后藤5并循环直到你完成
8)确保将所有内容都设置回退出时的状态,否则可能会将用户的键盘输入中断为X!
我建议用第二个usb键盘来测试这个,你可以用/dev/input和xinput来禁用和监听键盘,所以如果你崩溃了,你仍然有第一个键盘,并且正常工作。(实际上,我认为故意用第二个键盘来做这件事会很酷,热键加倍!)
但是,是的,需要根和潜在的离开键盘“分离”从X是不漂亮的,并与SendKey转发可能说起来容易做起来难,但我敢肯定,这 * 将 * 工作,并给予你最大的灵活性。

50few1ms

50few1ms6#

要编写一个键(快捷键)Map软件,还可以查看[libtermkey](http://leonerds-code.blogspot.de/2011/05/libtermkey-read-keypresses-from.html),它是一个终端键输入库(用C编写),可以识别XTerm样式的鼠标位置/按钮报告、特殊键(如箭头键和功能键),包括Ctrl-Left等“修改”键。
例如,[POE::Wheel::TermKey](http://cpansearch.perl.org/src/PEVANS/POE-Wheel-TermKey-0.02/README)“是libtermkey库的异步perl Package 器,它提供了一种抽象的方式来读取基于终端的程序中的按键事件。”

hgc7kmma

hgc7kmma7#

你可以使用XQueryKeymap来读取事件,然后你可以使用XTestKey来发送一个退格键事件,然后发送你想要按下的键。更好的是,你可以使用register hotkeys来读取所有键盘事件,然后使用XTestKey来生成键事件。顺便说一句,KDE的“自定义快捷键”控制模块允许使用快捷键来生成按键。Source code

3pmvbmvn

3pmvbmvn8#

虽然我知道这个问题已经过时了,但它仍然是相关的,而且我已经找到了一个比修改xorg-server更易移植的解决方法。
基本上,当抓取窗口是主动聚焦窗口时,XGrabKey不发送聚焦事件。
如果您观察活动窗口,并在焦点变为新窗口时重置抓取,则不会生成事件。我发现观察PropertyNotify事件和查看_NET_ACTIVE_WINDOW窗口属性可以很好地获取这些信息。
我还没有发现任何有害的影响,或坦率地说,任何影响,设置抓取是当前活动的窗口。

相关问题