Почему XGrabKey генерирует дополнительные события фокусировки и фокусировки?

Кто-нибудь знает функцию xlib для перехвата события нажатия клавиши без потери исходного фокуса? Как от этого избавиться?

(или «использовать XGrabKey () без генерации фокуса в стиле Grab»?)

(или «Как избавиться от событий фокуса 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 при изменении рабочего стола (одновременное изменение и отображение всплывающего диалогового окна) без вывода фокуса типа Grab на окно ... https://mail.gnome.org/archives/wm-spec-list/2007-May/msg00000.html

  5. ... Полноэкранный режим не должен выходить из событий FocusOut с помощью NotifyGrab ... https://bugzilla.mozilla.org/show_bug.cgi?id=578265

  6. захват клавиатуры не позволяет изменить фокус ... захват клавиатуры не позволяет изменить фокус

  7. События фокусировки, генерируемые захватами (как активный захват 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

И я хочу перенести его в Linux с помощью XGrabKey () и XUngrabKey (): 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


person Community    schedule 07.03.2013    source источник
comment
Вы ведь пишете кейлоггер? ;)   -  person Martin Thurau    schedule 07.03.2013
comment
Нет, я пишу программу сопоставления клавиш с помощью python-xlib: code.google.com/ p / diyism-myboard   -  person diyism    schedule 07.03.2013
comment
Если вы хотите переназначить клавиатуру, вы делаете это неправильно. XGrabKey не подходит для работы.   -  person n. 1.8e9-where's-my-share m.    schedule 08.03.2013
comment
Однажды (1997 год, я проверил) я написал кейлоггер, улавливающий ключевые события. Не знаю, компилируется ли он / работает или может вам пригодиться, но я создал суть, если вы хотите это проверить: gist.github.com/robertklep/5124355   -  person robertklep    schedule 09.03.2013
comment
@robertklep Спасибо, я нашел XSelectInput в вашем коде, может он лучше, чем XGrabKey, попробую.   -  person diyism    schedule 10.03.2013
comment
Я думаю, что комбинация XSelectInput с XNextEvent и XPutBackEvent может быть полезной, но вы смотрели xmodmap?   -  person robertklep    schedule 10.03.2013
comment
@robertklep xmodmap может отображать комбинацию клавиш только одной клавише, но не может сопоставлять одно нажатие клавиши комбинации клавиш и движению мыши.   -  person diyism    schedule 10.03.2013
comment
Но, возможно, его код может вам помочь :)   -  person robertklep    schedule 10.03.2013
comment
Не то же самое, xmodmap использует XChangeKeyboardMapping ()   -  person diyism    schedule 10.03.2013
comment
На самом деле есть причина, по которой, например, wxPython имеет [RegisterHotKey] [wiki.wxpython.org/RegisterHotKey], но только для Microsoft Windows, а не для Linux. Он подключается непосредственно к функциональности ОС, а не к оконному менеджеру. Вы искали в источнике X перехватчики для перехвата и перенаправления событий?   -  person Anthon    schedule 14.03.2013


Ответы (7)


Мой текущий код (из 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», фокус теряется, а когда я отпускаю ее, фокус возвращается.

person diyism    schedule 14.03.2013
comment
Ответ может заключаться в том, что в xlib нет решения. - person diyism; 15.03.2013
comment
Возможно, мы могли бы изменить исходный код XGrabKey, чтобы избавиться от событий фокусировки: cgit.freedesktop.org/xorg/lib/libX11/tree/src/GrKey.c - person diyism; 05.11.2013
comment
Ссылка myboard.py не работает, используйте это: http://web.archive.org/web/20160107234343/https://diyism-myboard.googlecode.com/files/myboard.py - person BladeMight; 26.03.2017

Я смотрел на глобальные горячие клавиши еще в начале 90-х для Irix, ultrix и solaris, поскольку это было легко сделать на моем компьютере Acorn BBC. В конце концов мы решили решить эту проблему непереносимым способом на уровне ниже xlib с некоторым проприетарным кодом. Поскольку установка нашего программного обеспечения в любом случае требовалась как привилегия суперпользователя, мы смогли вставить соответствующие программные хуки в качестве демонов.

Для Linux (в настоящее время) вам, вероятно, следует искать программное решение, обрабатывая событие клавиатуры на уровне операционной системы. Я бы начал с просмотра здесь: http://code.google.com/p/logkeys/

Более общим решением было бы иметь небольшую печатную плату с USB-входом и USB-выходом, которая действует для компьютера как мышь и клавиатура и при необходимости переводит клавиши клавиатуры. Но это было бы не так гибко, если вы хотите часто менять сопоставление.

person Anthon    schedule 10.03.2013

Наконец, как вы знаете, 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

И мне также нужно избавиться от XGrabKeyboard в контекстном меню gtk:

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

person Community    schedule 19.11.2013

Похоже, XQueryKeymap отсортирует вас. См. Ниже исходный код 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);
}

Обратите внимание, это не проверенный код, и он не мой - я просто нашел его в Интернете.

person hd1    schedule 10.03.2013
comment
скомпилирован и протестирован для меня - person Anthon; 11.03.2013
comment
Готово, я также исправил первую строку XQueryMap - ›XQueryKeymap и мне пришлось добавить несколько дополнительных символов для требуемого изменения редактирования 6 символов) - person Anthon; 11.03.2013
comment
Как только он пойман, вы несете ответственность за его отлов, @diyism - person hd1; 11.03.2013
comment
XQueryKeymap может улавливать нажатие клавиши (не только улавливать нажатие клавиши)? Как его поймать и не просочить в систему? - person diyism; 11.03.2013
comment
Он не захватывает событие, он позволяет вам просматривать очередь событий, не меняя ее. - person Anthon; 14.03.2013
comment
Как я уже сказал (выше), вы несете ответственность за его отлов, если хотите. - person hd1; 14.03.2013
comment
Разве этот ответ не должен предлагать какой-то способ уловить ключевое событие, поскольку это было то, о чем изначально просили? - person ws_e_c421; 16.02.2014

У меня есть идея, которая, я уверен, сработает, но мне нужно лечь спать, и я не могу проверить ее сам, и это некрасиво, так как я не думаю, что есть какой-либо способ сделать то, что вы хочу в 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, я бы просто вызвал его в оболочку для простоты реализации.

4) Откройте /dev/input/eventX, где X - клавиатура. На самом деле я бы поискал имя (указанное в xinput -list) в / dev / input / by-id и открыл его таким образом. Скорее всего, в какой-то момент для этого потребуется 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
}

ints - 32-битные, ushorts - 16-битные. Поскольку вас интересует только ввод с клавиатуры, вы можете сделать это довольно просто:

  • читать и игнорировать 8 байтов.

  • следующий байт должен быть 1, затем следующий - 0. Если нет, пропустите это событие

  • Следующий байт - это маленький конец ключевого кода, и, поскольку там ‹255 ключей, этого достаточно, так что

  • пропустить следующий байт.

  • прочитайте следующий байт, чтобы увидеть, нажат он или отпущен

  • пропустить следующие три байта

6) Когда вы получаете событие, которое вас интересует, справьтесь с ним самостоятельно. В противном случае используйте XSendEvent, чтобы отправить его в X, чтобы его можно было обработать нормально. Сопоставление аппаратного кода, который вы получаете из / dev / input, с соответствующим keyym может быть уловкой, но я почти уверен, что где-то в xlib есть функция, которая поможет с этим.

7) goto 5 и цикл, пока не закончите

8) Убедитесь, что вы установили все, как было при выходе, иначе вы можете сломать ввод с клавиатуры на X!

Я бы посоветовал протестировать это с помощью второй USB-клавиатуры, вы можете отключать и слушать клавиатуры независимо друг от друга с помощью / dev / input и xinput, поэтому в случае сбоя у вас все еще есть первая клавиатура и она работает нормально. (На самом деле, я думаю, что было бы неплохо намеренно сделать это со второй клавиатурой, удвоив горячие клавиши!)

Но да, нуждаться в root и потенциально оставлять клавиатуру «отделенной» от X - это совсем не красиво, и эту пересылку с помощью SendKey можно было бы легче сказать, чем сделать, но я почти уверен, что это будет работать и дать вам максимальную гибкость.

person Community    schedule 16.11.2013
comment
Спасибо, это разумно, но сложно. - person diyism; 16.11.2013

Для написания программного обеспечения для сопоставления клавиш (ярлыков) также ознакомьтесь с libtermkey, библиотекой ввода клавиш терминала (написанной на C), которая распознает информацию о положении / кнопках мыши в стиле XTerm, специальные клавиши (такие как стрелки и функциональные клавиши), включая " модифицированные "ключи типа Ctrl-Left.

Существует, например, POE::Wheel::TermKey, «асинхронная оболочка perl вокруг библиотеки libtermkey, которая обеспечивает абстрактный способ чтения событий нажатия клавиш в программах на базе терминала».

person Community    schedule 04.02.2014

Вы можете использовать XQueryKeymap для чтения событий, тогда вы можете использовать XTestKey, чтобы отправить событие клавиши Backspace, а затем клавишу, которую вы хотите нажать. Более того, вы можете зарегистрировать горячие клавиши для всех событий клавиатуры, а затем сгенерировать ключевое событие с помощью XTestKey. Кстати, модуль управления KDE «Custom Shortcuts» позволяет использовать сочетания клавиш для генерации нажатий клавиш. Исходный код.

person Community    schedule 16.11.2013
comment
XQueryKeymap может улавливать, но не может улавливать события. - person diyism; 16.11.2013
comment
@diyism Но я сказал, что можно сгенерировать событие возврата. Вы тот, кто проголосовал против? - person Ramchandra Apte; 16.11.2013
comment
@Apte, Backspace не отменяет движения мыши по срабатыванию. - person diyism; 16.11.2013
comment
@diyism, значит, вы хотите, чтобы было получено событие движения указателя, но не хотите, чтобы указатель перемещался? Я думал, вы только переназначаете клавиатуру. - person Ramchandra Apte; 16.11.2013