Подтвердить что ты не робот

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

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

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

(или "Как избавиться от событий NotifyGrab и NotifyUngrab для фокуса на уровне системы?)

XGrabKey потеряет фокус на нажатой клавише и восстановит фокус на нажатой клавише.

И я хочу уловить нажатие клавиши без ее утечки в исходное окно (так же, как XGrabKey может это сделать).

Литература:

Я нажимаю однократное нажатие на картографическое программное обеспечение с комбинацией клавиш (и мышью): 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

4b9b3361

Ответ 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 Модуль "Пользовательские ярлыки" позволяет использовать ярлыки для генерации нажатия клавиш. Исходный код.