-
Notifications
You must be signed in to change notification settings - Fork 0
/
read_kbd_direct.py
executable file
·191 lines (163 loc) · 6.89 KB
/
read_kbd_direct.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
#! /usr/bin/python3
'''
See: /usr/local/lib/python3.7/dist-packages/evdev/ecodes.py
Keys are defined here:
/usr/include/linux/input-event-codes.h
'''
import evdev
import threading
import sys
import time
class KDirect():
shift = False
ctrl = False
shift_keys = {'KEYS_CAPSLOCK': False} # create shift_keys; other keys added as they come
def __init__(self):
self.keyboard = self._find_keyboard()
if self.keyboard is None:
return
self._grab_keyboard() # My keyboard, plus stops local echo.
# Start flashing routine to flash Caps Lock led.
self.flash = threading.Thread(target=self._flash_ctl)
self.flash.start()
def _find_keyboard(self):
for _ in range(0, 2): # try a couple of times to find a keyboard
devices = [evdev.InputDevice(path) for path in evdev.list_devices()]
for device in devices:
# print(device.path, device.name, device.phys)
if "keyboard" in device.name.lower() and \
"control" not in device.name.lower():
print("Using", device.path, device.name)
return device
time.sleep(0.5) # pause before we scan again
return None
def _grab_keyboard(self):
try:
self.keyboard.grab() # My keyboard! Mine I say!
except OSError:
print("Someone else has the keyboard! Exiting!")
sys.exit()
def read_from(self):
while True:
try:
for event in self.keyboard.read_loop():
if event.type == evdev.ecodes.EV_KEY:
result = self._handle(event)
if result is None:
continue
return result
except OSError: # we have lost the keyboard
# except AttributeError: # we have lost the keyboard
del self.keyboard # device has gone, destroy it so flash can't use it.
self.keyboard = self._find_keyboard() # ... and rebuild it
if self.keyboard is None:
return '\r' # no recovery!
self._grab_keyboard()
def _handle(self, event):
# see values: /usr/local/lib/python3.7/dist-packages/
# evdev/events.py
# Constants
key_up = evdev.events.KeyEvent.key_up
key_down = evdev.events.KeyEvent.key_down
key_hold = evdev.events.KeyEvent.key_hold
shift_set = {'KEY_LEFTSHIFT','KEY_RIGHTSHIFT',
'KEY_LEFTCTRL', 'KEY_RIGHTCTRL', 'KEY_CAPSLOCK'}
char = {'GRAVE': ('`', '~'), 'MINUS': ('-', '_'), 'EQUAL': ('=', '+'),
'LEFTBRACE': ('[', '{'), 'RIGHTBRACE': (']','}'), 'BACKSLASH': ('\\', '|'),
'SEMICOLON': (';', ':'), 'APOSTROPHE': ("'", '"'), 'COMMA': (',', '<'),
'DOT': ('.','>'), 'SLASH': ('/', '?'),
'1': ('1', '!'), '2': ("2", '@'), '3': ('3' '#'), '4': ('4', '$'),
'5': ('5', '%'), '6': ("6", '^'), '7': ('7' '&'), '8': ('8', '*'),
'9': ('9', '('), '0': ('0' ')') }
spec_char = {'BACKSPACE': '\x7f', 'ESC': '\x1b',
'SPACE': ' ', 'ENTER': '\n', 'ctl_c': '\x03'}
# print(evdev.categorize(event))
# print(event.type, event.code, event.value)
key = evdev.ecodes.KEY[event.code]
# print(key)
if event.value == key_hold:
return # ignore
if key in shift_set:
# print(key)
self._shift_key(key, event.value==key_down)
return
if event.value == key_up:
return # ignore
# We have a real key press
# print(key)
symbol = key[4:] # trim off "KEY_"
if self.ctrl and symbol == "C": # ^C
return spec_char["ctl_c"]
if self.ctrl and symbol == "LEFTBRACE": # manual esc
return spec_char["ESC"]
if len(symbol) == 1 and symbol not in "0123456789": # then a letter key
return symbol if self.shift else symbol.lower() # symbol is retrieved
# as uppercase
if symbol in char: #
shift = 1 if self.shift else 0
return char[symbol][shift]
if symbol in spec_char:
return spec_char[symbol]
return # with None, and look for next input
def _shift_key(self, key, key_down):
# print(key)
if key_down and key == "KEY_CAPSLOCK":
self.shift_keys['KEYS_CAPSLOCK'] = \
not self.shift_keys['KEYS_CAPSLOCK']
else:
self.shift_keys[key] = key_down # True or False
self.shift = self.shift_keys['KEYS_CAPSLOCK']
# print(shift_keys)
for a_key in self.shift_keys: # search for any shift key that's down
if a_key[-5:] == 'SHIFT' and self.shift_keys[a_key]:
self.shift = True
break
if self.shift: # turn on capslock led
self._set_led(True)
self.flash_pause = True
else:
self.flash_pause = False # No shift keys on;
# ...will force set_led to start flashing.
self.ctrl = False
for a_key in self.shift_keys: # if any shift key is down
if a_key[-4:] == 'CTRL' and self.shift_keys[a_key]:
self.ctrl = True # find a ctrl key that is True
break
#-----------------------------------------------------------------------------
def _set_led(self, shine):
# set led to shine (True/False)
if self.keyboard is not None: # is None if we lose the keyboard spontaneously.
self.keyboard.set_led(evdev.ecodes.LED_CAPSL, shine)
#-----------------------------------------------------------------------------
#Globals
flashing = True
flash_pause = False
def _flash_ctl(self): # runs in separate thread (see init)
shine = True
while self.flashing:
time.sleep(1)
while self.flash_pause:
time.sleep(0.1)
self._set_led(shine)
shine = not shine
self._set_led(False)
#-----------------------------------------------------------------------------
def exit(self):
if self.keyboard is not None:
self.keyboard.ungrab()
self.flash_pause = False
self.flashing = False # causes _flash_ctl to terminate.
self.flash.join()
#-----------------------------------------------------------------------------
if __name__ == "__main__":
kbd = KDirect()
while True:
try:
key = kbd.read_from()
if key == '\n':
break
print(key, end="", flush=True)
except KeyboardInterrupt:
break
kbd.exit()
print("")