1 '''
2 Defines a class representing a standard L{Keyboard} input device and action
3 code constants used to register filters on that device.
4
5 @var MAX_KEY_CHORD: Maximum number of actions that can be in a keyboard gesture
6 @type MAX_KEY_CHORD: integer
7 @var def_keymap: Default keymap for translation between key codes, keysyms, and
8 key names
9 @type def_keymap: gtk.gdk.Keymap
10
11 @author: Peter Parente
12 @author: Scott Haeger
13 @organization: IBM Corporation
14 @copyright: Copyright (c) 2005, 2007 IBM Corporation
15
16 @author: Frank Zenker
17 @organization: IT Science Center Ruegen gGmbH, Germany
18 @copyright: Copyright (c) 2007, 2008 ITSC Ruegen
19
20 @license: I{The BSD License}
21 All rights reserved. This program and the accompanying materials are made
22 available under the terms of the BSD license which accompanies
23 this distribution, and is available at
24 U{http://www.opensource.org/licenses/bsd-license.php}
25 '''
26 from AccessEngine import AEInput
27 from AccessEngine import AEConstants
28 import pyatspi
29
30 import pygtk
31 pygtk.require('2.0')
32 import gtk.gdk as gdk
33 from Tools.i18n import _
34
35 DEBUG = False
36
37 def_keymap = gdk.keymap_get_default()
38 MAX_KEY_CHORD = 4
39
40 __uie__ = dict(kind='device')
41
43 '''
44 Gets the name of the given key code by using the default key map to lookup
45 its keysym value and then its localized name. If the code is not known, an
46 empty string is returned.
47
48 @param code: Hardware keycode
49 @type code: integer
50 @return: Name of the key
51 @rtype: string
52 '''
53 entries = def_keymap.get_entries_for_keycode(code)
54 if entries and len(entries):
55 return gdk.keyval_name(entries[0][0])
56 return ''
57
59 '''
60 Basic keyboard input device.
61
62 Has a reference to the L{AEEventManager
63 <AccessEngine.AEEventManager._AEEventManager>} to register for system
64 wide keyboard key press and key release events. Matches key presses against a
65 collection of stored L{AEInput.Gesture}s representing key combinations that
66 should be filtered before they can reach the window with the input focus.
67 Observers of this device are notified about L{AEInput.Gesture}s when one or
68 more press events are received followed by a release.
69
70 Instances of L{Keyboard} impose some constraints on the filters that can be
71 registered. A filter can contain at most one L{AEInput.Gesture} and that
72 L{AEInput.Gesture} may have at most L{MAX_KEY_CHORD} key codes and at least
73 one. The filtered gesture may start with zero, one, or two modifiers
74 registered via L{SystemInput.addModifier
75 <AccessEngine.AEDevice.AEInput.SystemInput.SystemInput.addModifier>} and must
76 end with exactly one non-modifier.
77
78 The L{Keyboard} device is robust against typomatic repeats and more than
79 L{MAX_KEY_CHORD} sustained presses. Typomatic repeats are ignored so
80 that holding two modifier keys, holding a typomatic key, releasing one of the
81 modifiers, and then releasing the typomatic key does not result in two
82 notifications. Key presses beyond the maximum number are also ignored without
83 affecting the current state.
84
85 @ivar filters: Registered L{AEInput.GestureList} filters keyed by the one and
86 only one L{AEInput.Gesture} allowed in each filter
87 @type filters: dictionary keyed by L{AEInput.Gesture}s
88 @ivar acc_reg: Reference to the C{pyatspi.Registry}
89 @type acc_reg: C{pyatspi.Registry}
90 @ivar curr_gesture: Actions seen so far that might form a filtered
91 L{AEInput.Gesture}
92 @type curr_gesture: L{AEInput.Gesture}
93 @ivar synth_press: Ordered list of synthesized key presses that have been
94 sent for processing and should be ignored when detected
95 @type synth_press: list
96 @ivar synth_release: Ordered list of synthesized key releases that have been
97 sent for processing and should be ignored when detected
98 @type synth_release: list
99 @ivar state: Current state method for the key event handling state machine
100 @type state: callable
101 @ivar last_press: Was the last event a press (True) or release (False)?
102 @type last_press: boolean
103 @ivar LOCK_KEYS: Key codes for keys that can be locked in a particular state,
104 namely L{AEK_CAPS_LOCK}, L{AEK_SCROLL_LOCK}, and L{AEK_NUM_LOCK}. These
105 are constant.
106 @type LOCK_KEYS: list of integer
107 @ivar ACC_KEYS: Key codes for keys that should never be consumed because they
108 activate accessibility features such as slow keys or sticky keys
109 @type ACC_KEYS: list of integer
110 @ivar EATEN_GESTURES: List of gestures commonly eaten by a party higher than
111 this device in the AT-SPI event chain (e.g. the window manager). This
112 device should not synthesize these gestures when detected.
113 @type EATEN_GESTURES: list of L{AEInput.Gesture}
114 '''
116 '''
117 Initializes the empty filters dictionary and creates an empty L{AEInput.Gesture} that will be used to
118 buffer incoming keystrokes until the L{AEInput.Gesture} definitely matches
119 or does not match one of the held filters. Registers for key press and
120 release events via C{pyatspi}. Sets the filtering mode to handled by
121 default. Also registers for window activate events to account for missing
122 keys caused by system wide hotkey presses.
123 '''
124 self.filters = {}
125 self.acc_reg = pyatspi.Registry
126 self.curr_gesture = AEInput.Gesture(self)
127 self.state = self._stateInvalid
128 self.synth_press = []
129 self.synth_release = []
130 self.last_press = False
131 self.LOCK_KEYS = [self.AEK_CAPS_LOCK, self.AEK_SCROLL_LOCK,
132 self.AEK_NUM_LOCK]
133 self.ACC_KEYS = [self.AEK_SHIFT_L, self.AEK_SHIFT_R]
134 self.EATEN_GESTURES = [
135 AEInput.Gesture(self,[self.AEK_ALT_L, self.AEK_F4]),
136 AEInput.Gesture(self,[self.AEK_ALT_R, self.AEK_F4])
137 ]
138
139
140
141 self.acc_reg.registerKeystrokeListener(self._onKeyPressed, mask=None, kind=(pyatspi.KEY_PRESSED_EVENT,))
142 self.acc_reg.registerKeystrokeListener(self._onKeyReleased, mask=None, kind=(pyatspi.KEY_RELEASED_EVENT,))
143 self.acc_reg.registerEventListener(self._onForegroundChange, 'window:activate')
144 self.setFilterMode(AEConstants.FILTER_HANDLED)
145
147 '''
148 Informs the L{AccessEngine.AEEventManager} that notifications about keyboard events are no
149 longer of interest to this L{Keyboard} device. Sets the filtering mode to
150 none for good measure. Removes the request for focus events also.
151 '''
152 AEInput.SystemInput.close(self)
153 self.acc_reg.deregisterKeystrokeListener(self._onKeyPressed, mask=None, kind=(pyatspi.KEY_PRESSED_EVENT,))
154 self.acc_reg.deregisterKeystrokeListener(self._onKeyReleased, mask=None, kind=(pyatspi.KEY_RELEASED_EVENT,))
155 self.acc_reg.deregisterEventListener(self._onForegroundChange,'window:activate')
156 self.setFilterMode(AEConstants.FILTER_NONE)
157
159 '''
160 Gets the list of capabilities provided by this device, namely 'keyboard.'
161
162 @return: 'keyboard' as the only capability of this device.
163 @rtype: list of string
164 '''
165 return ['keyboard']
166
168 '''
169 Adds a L{AEInput.GestureList} to the collection of filters. The fire
170 L{AEInput.Gesture} in the L{AEInput.GestureList} will be used when the mode
171 is set to filter handled to prevent keys consumed by SUE from reaching the
172 foreground application.
173
174 The L{Keyboard} input device only supports one L{AEInput.Gesture} per
175 filter, not a sequence of L{AEInput.Gesture}s, and only one non-modifier
176 key code per L{AEInput.Gesture} chord. Any L{AEInput.GestureList} breaking
177 these rules will cause this method to raise a ValueError.
178
179 @param key_filter: Filter to add to the filter list
180 @type key_filter: L{AEInput.GestureList}
181 @raise ValueError: When the given L{AEInput.GestureList} does not conform
182 to the rules set forth above
183 '''
184
185 if key_filter.getNumGestures() != 1:
186 raise ValueError('only one Gesture allowed in a Keyboard filter')
187 gesture = key_filter.getGestureAt(0)
188
189 if gesture.getNumActions() < 1:
190 raise ValueError('at least one key code required')
191
192 count = 0
193 for code in gesture:
194 try:
195 self.modifiers[code]
196 except KeyError:
197 count += 1
198 if count > 1:
199 raise ValueError('at most one non-modifier key allowed')
200
201 self.filters[gesture] = None
202
204 '''
205 Removes a L{AEInput.GestureList} from the collection of filters.
206
207 @param key_filter: Filter to remove from the filter list
208 @type key_filter: L{AEInput.GestureList}
209 @raise KeyError: When the L{AEInput.GestureList} to remove is not found
210 @raise IndexError: When the L{AEInput.GestureList} is empty
211 '''
212 del self.filters[key_filter.getGestureAt(0)]
213
215 '''
216 Removes all filters by destroying the dictionary and recreating it.
217 '''
218 self.filters = {}
219
221 '''
222 Gets the maximum L{AEInput.Gesture} chord length as defined by
223 L{MAX_KEY_CHORD}.
224
225 @return: Maximum L{AEInput.Gesture} chord length
226 @rtype: integer
227 '''
228 return MAX_KEY_CHORD
229
231 '''
232 Gets the name of this input device.
233
234 @return: Localized device name
235 @rtype: string
236 '''
237 return _('Keyboard')
238
240 '''
241 Resets the state of the L{Keyboard} by clearing out the L{curr_gesture} and
242 returning to L{_stateInvalid}.
243 '''
244 self.curr_gesture.clearActionCodes()
245 self.state = self._stateInvalid
246
247 - def _debug(self, header, code, event, press):
248 if DEBUG:
249 if press:
250 print 'press:', code
251 else:
252 print 'release:', code
253 print header
254 print 'next state:', self.state
255 print 'consumed:', event.consume
256 print 'curr key:', str(self.curr_gesture)
257 print 'synth pressed:', str(self.synth_press)
258 print 'synth released:', str(self.synth_release)
259 print '----------------------------'
260
262 '''
263 Peeks in the given gestures to see if it contains at least one modifier.
264
265 @param gesture: Gesture to check for a modifier
266 @type gesture: L{AEInput.Gesture}
267 @return: Does the gesture contain at least one modifier?
268 @rtype: boolean
269 '''
270 for code in gesture.peekActionCodes():
271 if self.modifiers.has_key(code):
272 return True
273 return False
274
276 '''
277 Peeks in the given gesture to see if it contains only modifiers.
278
279 @param gesture: Gesture to check for a non-modifier
280 @type gesture: L{AEInput.Gesture}
281 @return: Does the gesture contain at least one non-modifier?
282 @rtype: boolean
283 '''
284 for code in gesture.peekActionCodes():
285 if not self.modifiers.has_key(code):
286 return False
287 return True
288
290 '''
291 Synthesizes key strokes for the key codes in the given L{AEInput.Gesture}.
292 The presses are noted in L{synth_press} and L{synth_release} so that they
293 can be ignored when they are seen in L{_onKeyPressed} or L{_onKeyReleased}
294 to avoid duplicate processing.
295
296 @param press: Synthesize key presses (True) or releases (False)?
297 @type press: boolean
298 @param gesture: Gesture containing key codes to be pressed
299 @type gesture: L{AEInput.Gesture}
300 '''
301
302 if gesture in self.EATEN_GESTURES:
303
304 return
305
306
307 for code in gesture.peekActionCodes():
308 if code in self.LOCK_KEYS or code in self.ACC_KEYS:
309 continue
310 self._sendKey(code, press)
311
313 '''
314 Synthesizes a single key stroke. The presses are noted in L{synth_press}
315 and L{synth_release} so that they can be ignored when they are seen in
316 L{_onKeyPressed} or L{_onKeyReleased} to avoid duplicate processing.
317
318 @param hw_code: Keycode of the key to generate
319 @type hw_code: integer
320 @param press: Synthesize key press (True) or releas (False)?
321 @type press: boolean
322 '''
323 if press:
324 self.acc_reg.generateKeyboardEvent(hw_code, None, pyatspi.KEY_PRESS)
325 self.synth_press.append(hw_code)
326 else:
327 self.acc_reg.generateKeyboardEvent(hw_code, None, pyatspi.KEY_RELEASE)
328 self.synth_release.append(hw_code)
329
331 '''
332 Consumes the given event as long as it is not a special key used by other
333 system services for accessibility purposes such as shift for slow keys
334 and sticky keys.
335
336 @param event: Keyboard event
337 @type event: C{pyatspi.event.DeviceEvent}
338 @param press: Press or release event?
339 @type press: boolean
340 '''
341
342 event.consume = True
343 if press and (event.hw_code in self.ACC_KEYS):
344
345
346
347 self._sendKey(event.hw_code, True)
348
350 '''
351 Handles the use of stateful keys such as caps lock, num lock, and scroll
352 lock as modifiers. When a lock key is pressed, this method immediately
353 sets it back to its former state. When a lock key is released, this method
354 allows the change to pass but only if all of the conditions are met:
355
356 - The lock key was the only key in the L{curr_gesture} since it was last
357 empty.
358 - The lock key is not a gesture itself.
359 - The lock key is currently the only key in the L{curr_gesture}.
360
361 @param event: Keyboard event
362 @type event: C{pyatspi.event.DeviceEvent}
363 @param press: Was the event a press (True) or release (False)?
364 @type press: boolean
365 '''
366 hw_code = event.hw_code
367 if hw_code in self.LOCK_KEYS:
368 if press:
369
370 self._sendKey(hw_code, True)
371 elif (self.last_press == True and
372 not self.filters.has_key(self.curr_gesture) and
373 len(self.curr_gesture) == 1):
374
375
376
377 self._sendKey(hw_code, True)
378 self._sendKey(hw_code, False)
379
381 '''
382 Represents a state where the current key codes in L{curr_gesture} and the
383 next key press may lead to a match on one of the registered L{filters}.
384 The actions in this state are as follows.
385
386 If the event is a press or release of one of the registered
387 L{SystemInput.modifiers
388 <AccessEngine.AEDevice.AEInput.SystemInput.SystemInput.modifiers>},
389 the key is consumed and we remain in this state.
390
391 If the event is a press of a non-modifier and the L{curr_gesture} matches
392 one of the registered L{filters}, we consume the key and transition to
393 L{_stateMatch}.
394
395 If the event is anything else, we synthesize all keys in the
396 L{curr_gesture}, consume the current event, and transition to
397 L{_stateInvalid}.
398
399 @param event: Keyboard event
400 @type event: C{pyatspi.event.DeviceEvent}
401 @param press: Was the event a press (True) or release (False)?
402 @type press: boolean
403 @return: Method representing the next state
404 @rtype: callable
405 '''
406 if self.modifiers.has_key(event.hw_code):
407
408 self._consumeKey(event, press)
409 return self._statePossible
410 elif self.filters.has_key(self.curr_gesture):
411
412 self._consumeKey(event, press)
413 return self._stateMatch
414 else:
415
416 self._sendGesture(self.curr_gesture, True)
417
418 self._consumeKey(event, press)
419 return self._stateInvalid
420
422 '''
423 Represents a state where the current key codes in L{curr_gesture} and the
424 next key event is not likely to match one of the registered L{filters}.
425 The actions in this state are as follows.
426
427 If the event is a press resulting in the L{curr_gesture} having only
428 L{SystemInput.modifiers
429 <AccessEngine.AEDevice.AEInput.SystemInput.SystemInput.modifiers>} in it,
430 we consume the press and transition to L{_statePossible}.
431
432 If the event is a press resulting in the L{curr_gesture} exactly matching
433 one of the registered L{filters}, we consume the press transition to
434 L{_stateMatch}.
435
436 If the event is anything else, we allow the event to pass and stay in this
437 state.
438
439 @param event: Keyboard event
440 @type event: C{pyatspi.event.DeviceEvent}
441 @param press: Was the event a press (True) or release (False)?
442 @type press: boolean
443 @return: Method representing the next state
444 @rtype: callable
445 '''
446 if press:
447 if self._hasOnlyModifiers(self.curr_gesture):
448
449 self._consumeKey(event, press)
450 return self._statePossible
451 elif self.filters.has_key(self.curr_gesture):
452
453 self._consumeKey(event, press)
454 return self._stateMatch
455 elif len(self.synth_press):
456
457 self._consumeKey(event, press)
458 self._sendKey(event.hw_code, True)
459
461 '''
462 Represents a state where the current key codes in L{curr_gesture} matches
463 one of the L{filters} and the next event may determine if the key codes in
464 L{curr_gesture} are consumed forever or synthesized to be passed on to the
465 OS. The actions in this state are as follows.
466
467 If the event is a press and L{curr_gesture} matches one of the L{filters},
468 we stay in this state. (This case is needed to handle repeating, typomatic
469 presses of non-modifier keys.)
470
471 If the event is a press and L{curr_gesture} no longer matches one of the
472 L{filters}, we synthesize key presses for all keys in L{curr_gesture},
473 consume the current press, and transition to L{_stateInvalid}.
474
475 If the event is a release of one of the L{SystemInput.modifiers
476 <AccessEngine.AEDevice.AEInput.SystemInput.SystemInput.modifiers>}, we
477 consume the release and move to L{_stateAfterMatch}.
478
479 If the event is anything else (i.e. a non-modifier release), we consume the
480 key and transition to L{_statePossible}.
481
482 @param event: Keyboard event
483 @type event: C{pyatspi.event.DeviceEvent}
484 @param press: Was the event a press (True) or release (False)?
485 @type press: boolean
486 @return: Method representing the next state
487 @rtype: callable
488 '''
489 if press:
490 if self.filters.has_key(self.curr_gesture):
491
492 self._consumeKey(event, press)
493 return self._stateMatch
494 else:
495
496 self._sendGesture(self.curr_gesture, True)
497
498 self._consumeKey(event, press)
499 return self._stateInvalid
500 elif self.modifiers.has_key(event.hw_code):
501
502 self._consumeKey(event, press)
503 return self._stateAfterMatch
504 else:
505
506 self._consumeKey(event, press)
507 return self._statePossible
508
510 '''
511 Represents a state where the the keys in L{curr_gesture} just matched one
512 of the L{filters} and the next event was a release of one of the modifier
513 keys instead of the non-modifier. This state is needed to avoid typomatic
514 repeat presses of the still held non-modifier key. The actions in this
515 state are as follows.
516
517 If the event is a release of a non-modifier and the L{curr_gesture} is now
518 empty, we consume the release and move to L{_stateInvalid}.
519
520 If the event is a release of one of the non-modifiers and the
521 L{curr_gesture} still contains key strokes (e.g. L{SystemInput.modifiers
522 <AccessEngine.AEDevice.AEInput.SystemInput.SystemInput.modifiers>}), we consume
523 the release and move to L{_statePossible}.
524
525 @param event: Keyboard event
526 @type event: C{pyatspi.event.DeviceEvent}
527 @param press: Was the event a press (True) or release (False)?
528 @type press: boolean
529 @return: Method representing the next state
530 @rtype: callable
531 '''
532 if not press and not self.modifiers.has_key(event.hw_code):
533
534 self._consumeKey(event, press)
535 if self.curr_gesture.getNumActions() == 0:
536
537 return self._stateInvalid
538 else:
539
540 return self._statePossible
541 else:
542
543 self._consumeKey(event, press)
544
546 '''
547 Handles a key press event by acting on the hardware key code in
548 event.hw_code. If the key code is at the top of the L{synth_press} list,
549 then the press is ignored since it was synthesized and that code is removed
550 from the list. Otherwise, an attempt is made to add the key code to the
551 L{curr_gesture}. If the current gesture is full, the press is ignored and
552 the method returns immediately. If not, the event is dispatched to the
553 current state method (one of L{_statePossible}, L{_stateInvalid},
554 L{_stateMatch}). The return value of the state method is set as the next
555 state and the L{last_press} flag is set to True.
556
557 @param event: Keyboard key press event
558 @type event: C{pyatspi.event.DeviceEvent}
559 '''
560 hw_code = event.hw_code
561 if len(self.synth_press) and hw_code == self.synth_press[0]:
562
563 self.synth_press.pop(0)
564 self._debug('ignored our press', hw_code, event, True)
565 return
566 elif hw_code in self.synth_release:
567
568
569 event.consume = True
570 self._debug('ignored our press before release', hw_code, event, True)
571 return
572
573 if hw_code not in self.curr_gesture:
574
575 self.last_press = True
576
577
578 try:
579
580 self.curr_gesture.addActionCode(hw_code)
581 except ValueError:
582
583 self._debug('error adding', hw_code, event, True)
584 return
585
586 self._handleStateful(event, True)
587
588 self.state = self.state(event, True) or self.state
589 self._debug('state machine handled', hw_code, event, True)
590
592 '''
593 Handles a key release event by acting on the hardware key code in
594 event.hw_code. If the key code is at the top of the L{synth_release} list,
595 then the release is ignored since it was synthesized and that code is
596 removed from the list. Otherwise, the L{last_press} flag is checked to see
597 if it is True indicating the last event was a key press. If so, the
598 L{curr_gesture} is copied into a new L{AEInput.Gesture} and sent to any
599 listeners via the L{AEInput.SystemInput}._notifyInputListeners method. Next
600 an attempt is made to remove the key code from the L{curr_gesture}. If the
601 key code does not exist, the release is ignored and the method returns
602 immediately. If not, the event is dispatched to the current state method
603 (one of L{_statePossible}, L{_stateInvalid}, L{_stateMatch}). The return
604 value of the state method is set as the next state and the L{last_press}
605 flag is set to False.
606
607 @param event: Keyboard key release event
608 @type event: C{pyatspi.event.DeviceEvent}
609 '''
610 hw_code = event.hw_code
611 if len(self.synth_release) and hw_code == self.synth_release[0]:
612
613 self.synth_release.pop(0)
614 try:
615
616 self.curr_gesture.removeActionCode(hw_code)
617 except ValueError:
618 pass
619 self._debug('ignored our release', hw_code, event, False)
620 return
621 elif len(self.synth_press):
622
623 self._consumeKey(event, False)
624
625 self._sendKey(hw_code, False)
626 self._debug('ignored release before press', hw_code, event, False)
627 return
628 elif self.last_press:
629
630 gesture = AEInput.Gesture(self, gesture=self.curr_gesture)
631
632 self._notifyInputListeners(gesture, event.timestamp)
633
634
635 self._handleStateful(event, False)
636
637 try:
638
639 self.curr_gesture.removeActionCode(hw_code)
640 except ValueError:
641 self._debug('error removing', hw_code, event, False)
642 return
643
644 self.state = self.state(event, False) or self.state
645 self._debug('state machine handled', hw_code, event, False)
646 self.last_press = False
647
649 '''
650 Resets the internal key state on a window:activate event. This is needed to
651 resolve the problem of system wide hotkey releases going missing.
652
653 @see: U{http://bugzilla.gnome.org/show_bug.cgi?id=104058}
654 @param event: Keyboard key release event
655 @type event: C{pyatspi.event.DeviceEvent}
656 '''
657 self.resetState()
658
660 '''
661 Gets a string representation of all of the key codes in the given
662 L{AEInput.Gesture} using the L{_keyCodeToKeyName} function
663
664 @param gesture: L{AEInput.Gesture} to render as a string
665 @type gesture: L{AEInput.Gesture}
666 @return: String representation of the provided L{AEInput.Gesture}
667 @rtype: string
668 '''
669 return ','.join([_keyCodeToKeyName(i) for i in gesture.getActionCodes()])
670
671
672 AEK_ESCAPE = 9
673 AEK_QUESTION = 20
674 AEK_F1 = 67
675 AEK_F2 = 68
676 AEK_F3 = 69
677 AEK_F4 = 70
678 AEK_F5 = 71
679 AEK_F6 = 72
680 AEK_F7 = 73
681 AEK_F8 = 74
682 AEK_F9 = 75
683 AEK_F10 = 76
684 AEK_F11 = 95
685 AEK_F12 = 96
686 AEK_PRINT_SCREEN = 111
687 AEK_SCROLL_LOCK = 78
688 AEK_PAUSE = 110
689 AEK_TILDE = 49
690 AEK_1 = 10
691 AEK_2 = 11
692 AEK_3 = 12
693 AEK_4 = 13
694 AEK_5 = 14
695 AEK_6 = 15
696 AEK_7 = 16
697 AEK_8 = 17
698 AEK_9 = 18
699 AEK_0 = 19
700 AEK_MINUS = 20
701 AEK_EQUALS = 21
702 AEK_BACK_SPACE = 22
703 AEK_TAB = 23
704 AEK_Q = 24
705 AEK_W = 25
706 AEK_E = 26
707 AEK_R = 27
708 AEK_T = 28
709 AEK_Y = 29
710 AEK_U = 30
711 AEK_I = 31
712 AEK_O = 32
713 AEK_P = 33
714 AEK_OPEN_BRACKET = 34
715 AEK_CLOSE_BRACKET = 35
716 AEK_BACK_SLASH = 51
717 AEK_CAPS_LOCK = 66
718 AEK_A = 38
719 AEK_S = 39
720 AEK_D = 40
721 AEK_F = 41
722 AEK_G = 42
723 AEK_H = 43
724 AEK_J = 44
725 AEK_K = 45
726 AEK_L = 46
727 AEK_SEMICOLON = 47
728 AEK_QUOTE = 48
729 AEK_ENTER = 36
730 AEK_SHIFT_L = 50
731 AEK_Z = 52
732 AEK_X = 53
733 AEK_C = 54
734 AEK_V = 55
735 AEK_B = 56
736 AEK_N = 57
737 AEK_M = 58
738 AEK_COMMA = 59
739 AEK_PERIOD = 60
740 AEK_SLASH = 61
741 AEK_SHIFT_R = 62
742 AEK_CONTROL_L = 37
743 AEK_ALT_L = 64
744 AEK_SPACE = 65
745 AEK_ALT_R = 113
746 AEK_MENU = 117
747 AEK_CONTROL_R = 109
748 AEK_LEFT = 100
749 AEK_RIGHT = 102
750 AEK_UP = 98
751 AEK_DOWN = 104
752 AEK_PAGE_DOWN = 105
753 AEK_PAGE_UP = 99
754 AEK_DELETE = 107
755 AEK_INSERT = 106
756 AEK_HOME = 97
757 AEK_END = 103
758 AEK_NUM_LOCK = 77
759 AEK_DIVIDE = 112
760 AEK_MULTIPLY = 63
761 AEK_ADD = 86
762 AEK_SUBTRACT = 82
763 AEK_DECIMAL = 91
764 AEK_NUMPAD0 = 90
765 AEK_NUMPAD1 = 87
766 AEK_NUMPAD2 = 88
767 AEK_NUMPAD3 = 89
768 AEK_NUMPAD4 = 83
769 AEK_NUMPAD5 = 84
770 AEK_NUMPAD6 = 85
771 AEK_NUMPAD7 = 79
772 AEK_NUMPAD8 = 80
773 AEK_NUMPAD9 = 81
774 AEK_NUMPAD_ENTER = 108
775 AEK_THINKPAD_FN = 227
776 AEK_THINKPAD_BACK = 234
777 AEK_THINKPAD_FORWARD = 233
778