× К оглавлению На главную Об авторе

Дата и время публикации   :

Проблема и решение


1. Суть проблемы

В преобразовании сканкодов клавиш клавиатуры к кодам нажатия в Gtk3 кажется много таинственного и необычного, на первый взгляд. Почему? Потому что, например заявленные, как физический код клавиш, значение возврашаемое в hardware_keycode не совпадает ни с одним из трех наборов сканкодов, которые может генерить клавиатура и пречисленных найти на www.win.tue.nl. Ниже приводится листинг 1.1 получения сканкодов для Vte.Terminal

Листинг 1.1

...
162 class GVTerminal(Vte.Terminal) :
...
166     def __init__(self,  *args, **keywords) : 
…
169          self.connect('key-press-event', self.on_key_press_event )
…
183      def on_key_press_event(self, widget, event):
184           print("Key press on widget: ", widget )
185           print("     Modifiers       : ", event.state )
186           print("     Hardware keycode: %x" % event.hardware_keycode )
187           print("     Key val         : %x" % event.keyval ) 
188           print("     Key name        : %s" % Gdk.keyval_name(event.keyval) ) 
189           print("     Key unicode     : %x" % Gdk.keyval_to_unicode(event.keyval) ) 
190
191         return
... 

В строке 183 которого, определена функция-обработчик нажатия клавиш по событию 'key-press-event', как показано в строке 169 того же листинга.

В результате реализации которого получим следующие волшебные числа, например для Bacspace, Escape, Return, он же Enter и Space, как показано в дампе 1.2, ниже.

Листинг 1.2

...
Key press on widget:  <GTerminal.GVTerminal object at 0x7f09aef44d00 (GTerminal+GVTerminal at 0x2b55580)>
     Modifiers:   <flags 0 of type Gdk.ModifierType>
     Hardware keycode: 24
     Key val: ff0d
     Key name: Return
      Key unicode: d
Key press on widget:   <GTerminal.GVTerminal object at 0x7f09aef44d00 (GTerminal+GVTerminal at 0x2b55580)>
     Modifiers:   <flags 0 of type Gdk.ModifierType>
     Hardware keycode: 33
     Key val: 5c
     Key name: backslash
      Key unicode: 5c
Key press on widget:   <GTerminal.GVTerminal object at 0x7f09aef44d00 (GTerminal+GVTerminal at 0x2b55580)>
     Modifiers:   <flags 0 of type Gdk.ModifierType>
     Hardware keycode: 9
     Key val: ff1b
     Key name: Escape
      Key unicode: 1b
Key press on widget:   <GTerminal.GVTerminal object at 0x7f09aef44d00 (GTerminal+GVTerminal at 0x2b55580)>
     Modifiers:   <flags 0 of type Gdk.ModifierType>
     Hardware keycode: 41
     Key val: 20
     Key name: space
     Key unicode: 20
...

Убежден, что значение event.hardware_keycode напечатанное на против Hardware keycode для четырех часто используемых клавиш может свести с ума. При этом,

Дамп 1.3

...
Key press on widget:  <GTerminal.GVTerminal object at 0x7f09aef44d00 (GTerminal+GVTerminal at 0x2b55580)>
     Modifiers:  <flags 0 of type Gdk.ModifierType>
     Hardware keycode: 6f
     Key val: ff52
     Key name: Up
     Key unicode: 0
Key press on widget:  <GTerminal.GVTerminal object at 0x7f09aef44d00 (GTerminal+GVTerminal at 0x2b55580)>
     Modifiers:  <flags 0 of type Gdk.ModifierType>
     Hardware keycode: 51
     Key val: ff9a
     Key name: KP_Page_Up
     Key unicode: 0

Из которого видно, что значение обоих клавиш в юникоде равно нулю, а физический код не совпадает с ни одним из наборов сканкодов, которые можно найти на www.win.tue.nl

2. Решение

Поэтому взглянем на проблему, вооружившись некоторыми знаниями. Первое получим название клавиатурной раскладки – обычно pc(pc105) или pc(pc104), которую можно получить следующим образом:

$ setxkbmap -print | grep geometry
	xkb_geometry  { include "pc(pc105)"	};

Что указывает на один из набор клавиш IBM/PC, который перекочевал в современные десктопы и ноутбуки.

Далее определяем набор сканкода клавиш IBM с помощью команды showkey -s и определяем сканкод

При этом, как написано на www.win.tue.nl первое значение для клавиши Esc (0x01) , второе (0x81) – отпусканию клавиши, что между обеими значениями составляет разницу 0x80 или логическую единицу в старшем разряде.

Соответственно, как следует из ранее полученных значений нажатия клавиш Gtk, приведем соотношения сканкодов нажатия клавиш утилиты showkey и физический код, полученный после наступления события нажатия клавиши "key-press-event", как показано в таблице 2.1

Таблица 2.1
Клавиша Утилита showkey, сканкод нажатия Gdk.event, физический код клавиши Разница
клавиши Esc 0x01 0x09 0x08
клавиши Enter 0x1c 0x24 0x08
клавиши Space 0x39 0x41 0x08
клавиши ArrowUp 0xe0 0x6f 0x08
клавиши PageUp 0x49 0x51 0x08

из которой видно, что физический код клавиши Gdk.event и реальный сканкод нажатия имеют разницу 0x08, о которой просто нужно помнить. Соответственно для выдачи команд можно пользоваться этими кодами, но для лучшей читаемости кода и траты времени на документирование, предлагаю использовать имена клавиш, пример которых приведен в таблицe 2.2

Таблица 2.2
Клавиша Физический код клавиши Gdk.event Уникальное значение клавиши Gdk.event, GDK_KEY_* Символьное значение ascii, принимаемое терминалом xterm(-256color)
клавиша Esc 0x09 Escape '\e'
клавиша Enter 0x24 Enter '\n'
клавиша Space 0x41 space '\x20' или ' '
Клавиша Home 0x6e Home '\e[H'
клавиша Arrow Up 0x6f Up '\e[A'
клавиша Arrow Down 0x74 Down '\e[B'
клавиша Page Up 0x51 KP_Page_Up '\e[5~'
клавиша Page Down 0x59 KP_Next '\e[6~'
Клавиша End 0x73 End '\e[F'
Клавиша Del 0x77 Delete '\e[3~'
Клавиша Ins 0x76 Insert '\e[2~'

На основе приведенной таблице 2.2, в реализуемом коде терминала декларируем список JSON, как показано в дампе 2.3

Дамп 2.3

...
     jsonKeynames = {
                      "Insert"     : '\033[2~', 
                      "Delete"     : '\033[3~',
                      "KP_Next"    : '\033[6~', 
                      "KP_Page_Up" : '\033[5~',
                      "End"        : '\033[F',
                      "Home"       : '\033[H',
                      "Left"       : '\033[D',
                      "Right"      : '\033[C',
                      "Down"       : '\033[B',
                      "Up"         : '\033[A',
                      "Escape"     : '\033',  
                      "Tab"        : '\t',
                      "BackSpace"  : '\b',
                      "Enter"      : '\n',
                      "space"      : '\x20',
...
                      "question"   : '?',
                      "at"         : '@',
                     }
...
 def __init__(self,  *args, **keywords) : 
          Vte.Terminal.__init__(self, *args, **keywords)
          self.set_input_enabled(True)
          self.connect('key-press-event', self.on_key_press_event )
...
def on_key_press_event(self, widget, event):
...
           keyname = Gdk.keyval_name(event.keyval)
...
           if keyname in self.keynames : 
              print("     Terminal command: %s" % self.keynames[keyname] ) 
...

В котором показано, что если клавиша из JSON-списка была нажата, она считается терминальной командой или транслируемым символом из ряда '!','@','#','$','%','$','%','^','&','*','(',')' и т.д.

Но, этого оказалось мало, потому что нужно еще определять к какому типу относится нажатая клавиша, а именно к :

Для этого, казалось бы можно было использовать так называемые модификаторы поглощения (consumed modifiers), которые относятся к модификаторы клавиш, маскирующим состояние события Gdk.event.state для определения нажатия комбинаций клавиш и определения используемого набора.

К таким модификаторам клавиш относятся Shift, Control, Meta, Super (клавиша со со значком Windows), Hyper, Alt, Apple и т.д. насколько хватит фантазии у разработчиков раскладок клавиатур.

При этом, для распознавания клавиш по вышеперечисленным типам нам понадобятся при нажатой:

При этом нужно учитывать, что

3. Библиография

3.1 AskUbuntu.How do I change keyboard geometry from 104 to 105 keys?

3.2 Keyboard scancodes in linux

3.3 StackOverflow.Map keycode to physical location

3.4 Map table of the scancode/hardware key code

3.5 StackOverflow. Casing arrow keys in bash

3.6 Unix&Linux. Where do I find a list of terminal key codes to remap shortcuts in bash?

3.7 Microsoft.Using your keyboard

3.8 AskUbuntu. What are the meta, super, and hyper keys?

3.9 Unofficial Dub Package Documentation. gdk.Keymap.d