Почему XGrabKey генерирует дополнительные фокусы и фокус-события?
Кто-нибудь знает функцию xlib для захвата события нажатия клавиши без потери первоначального фокуса? Как избавиться от него?
(или "использовать XGrabKey() без генерации фокуса в стиле захвата"?)
(или "Как избавиться от событий NotifyGrab и NotifyUngrab для фокуса на уровне системы?)
XGrabKey потеряет фокус на нажатой клавише и восстановит фокус на нажатой клавише.
И я хочу уловить нажатие клавиши без ее утечки в исходное окно (так же, как XGrabKey может это сделать).
Литература:
-
... XGrabKey украдет фокус...
https://bugs.launchpad.net/gtkhotkey/+bug/390552/comments/8
-
... Программа получает контроль, чтобы что-то сделать в ответ на комбинацию клавиш. Между тем, программа была временно сфокусирована...
Во время XGrabKey (доска) откройте, какое окно было сфокусировано
-
... Функция 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
-
... Полноэкранный режим не должен выходить из событий FocusOut с NotifyGrab...
https://bugzilla.mozilla.org/show_bug.cgi?id=578265
-
захватывающая клавиатура не позволяет изменять фокус...
захватывающая клавиатура не позволяет изменять фокус
-
Фокусные события, созданные 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
И я хочу перенести его в 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
Ответы
Ответ 1
Наконец, как вы знаете, 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
Ответ 2
Я посмотрел на глобальные горячие клавиши еще в начале 90-х для Irix, ultrix и solaris, поскольку это было легко сделать на моем компьютере Acorn BBC. В конце концов мы решили решить это не переносимым образом на уровне ниже xlib с некоторым проприетарным кодом.
Так как наша установка программного обеспечения была необходима как привилегированный суперпользователь, мы смогли вставить соответствующие программные перехватчики в качестве демонов.
Для Linux (в настоящее время) вы, вероятно, должны искать программное решение, обрабатывая событие клавиатуры на уровне os. Я бы начал с того, чтобы посмотреть здесь: http://code.google.com/p/logkeys/
Более общим решением было бы иметь небольшую плату с USB и USB-выходом, которая действует на компьютер как мышь и клавиатура и при необходимости переводит клавиатурные клавиши. Но это не будет настолько гибким, если вы хотите часто менять отображение.
Ответ 3
Мой текущий код (из 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", фокус теряется, и когда я его отпускаю, фокус возвращается.
Ответ 4
Похоже, XQueryKeymap сортирует вас. Ниже приведено исходный код С++, который я нашел:
/* 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);
}
Обратите внимание, что это не проверенный код, и он не мой - я просто нашел его в Интернете.
Ответ 5
У меня есть идея, что я уверен, что это сработает, но я должен ложиться спать и не могу проверить ее сам, и это некрасиво, так как я не думаю, что есть способ делай то, что хочешь в 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. Если нет, пропустите это событие
-
Следующий байт является маленьким концом key code, и так как там < 255 клавиш, что достаточно хорошо, чтобы
-
пропустить следующий байт.
-
прочитайте следующий байт, чтобы узнать, нажата ли кнопка нажата или выпущена
-
пропустить следующие три байта
6) Когда вы получаете событие, которое вас интересует в ловушке, обработайте его самостоятельно. В противном случае используйте XSendEvent, чтобы отправить его на X, чтобы его можно было обрабатывать в обычном режиме. Сопоставление аппаратного кода, который вы получаете от /dev/input, к соответствующему keyym, может быть трюком, но я уверен, что там где-то есть функция в xlib, чтобы помочь с этим.
7) goto 5 и цикл, пока вы не закончите
8) Убедитесь, что вы все вернули, как это было, когда вы выходите, или вы можете сломать ввод пользовательской клавиатуры на X!
Я бы предложил протестировать это с помощью второй клавиатуры usb, вы можете отключить и слушать клавиатуру независимо друг от друга с помощью /dev/input и xinput, поэтому, если вы потерпите крах, у вас все еще есть первая клавиатура и работает нормально. (На самом деле, я думаю, было бы здорово сделать это со второй клавиатурой, удвоить горячие клавиши!)
Но да, нуждаясь в корне и потенциально оставляя клавиатуру "отстраненной" от X, совсем не красивы, и что пересылка с помощью SendKey может быть проще сказать, чем сделать, но я уверен, что это сработает и даст максимальная гибкость.
Ответ 6
Для написания программного обеспечения для сопоставления клавиш (ярлыка) также смотрите libtermkey
, библиотеку ввода терминального ключа (написанную на языке C), которая распознает сообщение мыши/кнопки мыши в стиле XTerm, специальные клавиши (такие как стрелка и функция ключи), включая "измененные" клавиши, такие как Ctrl-Left
.
Существует, например, POE::Wheel::TermKey
, асинхронная Perl-оболочка вокруг библиотеки libtermkey
, которая предоставляет абстрактный способ чтения событий нажатия клавиш в программах на основе терминалов.
Ответ 7
Вы можете использовать XQueryKeymap для чтения событий, тогда вы можете использовать XTestKey, чтобы отправить событие backspace key, а затем клавишу, которую вы хотите нажать. Лучше, вы могли зарегистрировать горячие клавиши для всех событий клавиатуры, а затем сгенерировать ключевое событие с помощью XTestKey. BTW, KDE Модуль "Пользовательские ярлыки" позволяет использовать ярлыки для генерации нажатия клавиш. Исходный код.