-
Notifications
You must be signed in to change notification settings - Fork 0
/
keyboard_102_modded_capslock_numlock.py
255 lines (223 loc) · 13.8 KB
/
keyboard_102_modded_capslock_numlock.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
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
#!/usr/bin/python3
import RPi.GPIO as GPIO
from time import sleep
from evdev import UInput, ecodes as e
# Logging removed for improving keystroke response
import logging
# ================= Special keyboard handling [GW] =================
# Support for Num-Lock with the numlock_keymap. This requires a modified Tandy keyboard that has the Num-Lock
# mechanical locking keyswitch replaced with a momentary-contact keyswitch, like that of most keys on the keyboard.
# This will mean a user can press Num-lock it will simply switch a state, not remain locked down in the ON position.
# numlock_keymap[] contains the modified keyboard layout including the numeric keys for m,j,k,l,u,i and o.
#
# I also tried to create a whole new keymap for when the CODE key is held, but since most of the keys require two
# events to be generated (shift + some other key), there's no way to represent that in a keymap. Thus I had to create
# special logic for each of these:
#
# SHIFT BS = "DEL": unpress KEY_LEFTSHIFT, press KEY_DELETE
# SHIFT [ = "]" : unpress KEY_LEFTSHIFT, press KEY_RIGHTBRACE
# CODE 1 = "|" : press KEY_LEFTSHIFT + KEY_BACKSLASH
# CODE 9 = "{" : press KEY_LEFTSHIFT + KEY_LEFTBRACE
# CODE 0 = "}" : press KEY_LEFTSHIFT + KEY_RIGHTBRACE
# CODE / = "\" : press KEY_BACKSLASH (this could have been in a new keymap, but why create a whole keymap for only one key?)
#logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')
#logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
#logging.basicConfig(level=logging.ERROR, format='%(asctime)s - %(levelname)s - %(message)s')
logging.basicConfig(level=logging.CRITICAL, format='%(asctime)s - %(levelname)s - %(message)s')
ui = UInput(name = "Tandy 102 Keyboard", vendor = 0x01, product = 0x01)
# Tandy 102 Keyboard pin to GPIO pin map
# 1 : 11, 10 : 23,
# 2 : 12, 11 : 29,
# 3 : 13, 12 : 31,
# 4 : 15, 13 : 32,
# 5 : 16, 14 : 33,
# 6 : 18, 15 : 35,
# 7 : 19, 16 : 36,
# 8 : 21, 17 : 37,
# 9 : 22, 18 : 0
cols = [11,12,13,15,16,18,19,21,22]
rows = [23,29,31,32,33,35,36,37]
keymap = [
e.KEY_Z, e.KEY_A, e.KEY_Q, e.KEY_O, e.KEY_1, e.KEY_9, e.KEY_SPACE, e.KEY_F1, e.KEY_LEFTSHIFT,
e.KEY_X, e.KEY_S, e.KEY_W, e.KEY_P, e.KEY_2, e.KEY_0, e.KEY_BACKSPACE, e.KEY_F2, e.KEY_LEFTCTRL,
e.KEY_C, e.KEY_D, e.KEY_E, e.KEY_LEFTBRACE, e.KEY_3, e.KEY_MINUS, e.KEY_TAB, e.KEY_F3, e.KEY_LEFTALT,
e.KEY_V, e.KEY_F, e.KEY_R, e.KEY_SEMICOLON, e.KEY_4, e.KEY_EQUAL, e.KEY_ESC, e.KEY_F4, e.KEY_FN,
e.KEY_B, e.KEY_G, e.KEY_T, e.KEY_APOSTROPHE, e.KEY_5, e.KEY_LEFT, e.KEY_GRAVE, e.KEY_F5, e.KEY_NUMLOCK,
e.KEY_N, e.KEY_H, e.KEY_Y, e.KEY_COMMA, e.KEY_6, e.KEY_RIGHT, e.KEY_COPY, e.KEY_F6, e.KEY_CAPSLOCK,
e.KEY_M, e.KEY_J, e.KEY_U, e.KEY_DOT, e.KEY_7, e.KEY_UP, e.KEY_CLEAR, e.KEY_F7, e.KEY_RESERVED,
e.KEY_L, e.KEY_K, e.KEY_I, e.KEY_SLASH, e.KEY_8, e.KEY_DOWN, e.KEY_ENTER, e.KEY_F8, e.KEY_PAUSE,
]
numlock_keymap = [
e.KEY_Z, e.KEY_A, e.KEY_Q, e.KEY_6, e.KEY_1, e.KEY_9, e.KEY_SPACE, e.KEY_F1, e.KEY_LEFTSHIFT,
e.KEY_X, e.KEY_S, e.KEY_W, e.KEY_P, e.KEY_2, e.KEY_0, e.KEY_BACKSPACE, e.KEY_F2, e.KEY_LEFTCTRL,
e.KEY_C, e.KEY_D, e.KEY_E, e.KEY_LEFTBRACE, e.KEY_3, e.KEY_MINUS, e.KEY_TAB, e.KEY_F3, e.KEY_LEFTALT,
e.KEY_V, e.KEY_F, e.KEY_R, e.KEY_SEMICOLON, e.KEY_4, e.KEY_EQUAL, e.KEY_ESC, e.KEY_F4, e.KEY_FN,
e.KEY_B, e.KEY_G, e.KEY_T, e.KEY_APOSTROPHE, e.KEY_5, e.KEY_LEFT, e.KEY_GRAVE, e.KEY_F5, e.KEY_NUMLOCK,
e.KEY_N, e.KEY_H, e.KEY_Y, e.KEY_COMMA, e.KEY_6, e.KEY_RIGHT, e.KEY_COPY, e.KEY_F6, e.KEY_CAPSLOCK,
e.KEY_0, e.KEY_1, e.KEY_4, e.KEY_DOT, e.KEY_7, e.KEY_UP, e.KEY_CLEAR, e.KEY_F7, e.KEY_RESERVED,
e.KEY_3, e.KEY_2, e.KEY_5, e.KEY_SLASH, e.KEY_8, e.KEY_DOWN, e.KEY_ENTER, e.KEY_F8, e.KEY_PAUSE,
]
GPIO.setmode(GPIO.BOARD)
for row in rows:
logging.debug(f"Setting pin {row} as an output")
GPIO.setup(row, GPIO.OUT)
for col in cols:
logging.debug(f"Setting pin {col} as an input")
GPIO.setup(col, GPIO.IN, pull_up_down = GPIO.PUD_DOWN)
# Set object to store pressed keys
pressed = set()
# Normal polling rate
sleep_time = 1/60
# Keep track of how many keyboard polls since the last keypress
polls_since_press = 0
# Keep track of the numlock state, default to off
num_lock = 0
# Keep track of keys modified with SHIFT and CODE keys
shifted_key = 0
coded_key = 0
while True:
sleep(sleep_time)
syn = False
for i in range(len(rows)):
logging.debug(f"Setting row {i} high, pin {rows[i]}")
GPIO.output(rows[i], GPIO.HIGH)
for j in range(len(cols)):
# Look up the keycode in our map
keycode = i * (len(rows) + 1) + j
logging.debug(f"Checking column {j}, pin {cols[j]} which results in key {keymap[keycode]}")
newval = GPIO.input(cols[j]) == GPIO.HIGH
# ========================================================================================================================
# Detect a newly pressed key (Is our pressed key not yet in the set of pressed keys?)
# ========================================================================================================================
if newval and not keycode in pressed:
# Add it to the set
pressed.add(keycode)
# ---------------------------------------------------------
# NUM-LOCK handler
# ---------------------------------------------------------
if keycode == 44:
if num_lock == 0:
num_lock = 1
logging.info(f"Pressed {keycode} - Set num_lock = 1")
else:
num_lock = 0
logging.info(f"Pressed {keycode} - Set num_lock = 0")
# ---------------------------------------------------------
# Handling for SHIFT BS, SHIFT [, and CODE modifiers
# ---------------------------------------------------------
# SHIFT BS - Generate DEL
if keycode == 15 and 8 in pressed:
# Release the SHIFT key and send DEL instead
logging.info(f"Pressed {keycode} but actually press e.KEY_DELETE instead due to SHIFT key")
ui.write(e.EV_KEY, e.KEY_LEFTSHIFT, 0)
ui.write(e.EV_KEY, e.KEY_DELETE, 1)
shifted_key = e.KEY_DELETE
# SHIFT [ - Generate right brace
elif keycode == 21 and 8 in pressed:
# Release the SHIFT key and send right brace instead
logging.info(f"Pressed {keycode} but actually press e.KEY_RIGHTBRACE instead due to SHIFT key")
ui.write(e.EV_KEY, e.KEY_LEFTSHIFT, 0)
ui.write(e.EV_KEY, e.KEY_RIGHTBRACE, 1)
shifted_key = e.KEY_RIGHTBRACE
# CODE / - Generate backslash
elif keycode == 66 and 35 in pressed:
# Send Backslash instead of /
logging.info(f"Pressed {keycode} but actually send e.KEY_BACKSLASH instead due to CODE key")
ui.write(e.EV_KEY, e.KEY_BACKSLASH, 1)
coded_key = e.KEY_BACKSLASH
# CODE 1 - Generate verticle bar
elif keycode == 4 and 35 in pressed:
# Send SHIFT \ to get a |
logging.info(f"Pressed {keycode} but actually send e.KEY_LEFTSHIFT + e.KEY_BACKSLASH instead due to CODE key")
ui.write(e.EV_KEY, e.KEY_LEFTSHIFT, 1)
ui.write(e.EV_KEY, e.KEY_BACKSLASH, 1)
coded_key = e.KEY_BACKSLASH
# CODE 9 - Generate Left curly brace
elif keycode == 5 and 35 in pressed:
# Send SHIFT [ to get a {
logging.info(f"Pressed {keycode} but actually send e.KEY_LEFTSHIFT + e.KEY_LEFTBRACE instead due to CODE key")
ui.write(e.EV_KEY, e.KEY_LEFTSHIFT, 1)
ui.write(e.EV_KEY, e.KEY_LEFTBRACE, 1)
coded_key = e.KEY_LEFTBRACE
# CODE 0 - Generate Right curly brace
elif keycode == 14 and 35 in pressed:
# Send SHIFT ] to get a }
logging.info(f"Pressed {keycode} but actually send e.KEY_LEFTSHIFT + e.KEY_RIGHTBRACE instead due to CODE key")
ui.write(e.EV_KEY, e.KEY_LEFTSHIFT, 1)
ui.write(e.EV_KEY, e.KEY_RIGHTBRACE, 1)
coded_key = e.KEY_RIGHTBRACE
# ---------------------------------------------------------
# Regular handler using keymap[] and numlock_keymap[]
# ---------------------------------------------------------
else:
# Check for num-lock being set; if so use numlock_keymap
if num_lock:
logging.info(f"Pressed {keycode} which is num-locked key {e.KEY[numlock_keymap[keycode]]} Column {i} Row {j}")
ui.write(e.EV_KEY, numlock_keymap[keycode], 1)
# Otherwise use regular keymap
else:
logging.info(f"Pressed {keycode} which is key {e.KEY[keymap[keycode]]} Column {i} Row {j}")
ui.write(e.EV_KEY, keymap[keycode], 1)
syn = True
# ========================================================================================================================
# Detect if the key is released (If there was a state change, was our pressed key in the set of pressed keys?)
# ========================================================================================================================
elif not newval and keycode in pressed:
# Record the released key state to the system - process all exceptions
if keycode == 15 and 8 in pressed:
logging.info(f"Released {keycode} but actually release e.KEY_DELETE due to SHIFT key")
ui.write(e.EV_KEY, e.KEY_DELETE, 0)
elif keycode == 21 and 8 in pressed:
logging.info(f"Released {keycode} but actually release e.KEY_RIGHTBRACE due to SHIFT key")
ui.write(e.EV_KEY, e.KEY_RIGHTBRACE, 0)
elif keycode == 66 and 35 in pressed:
logging.info(f"Released {keycode} but actually release e.KEY_BACKSLASH instead due to CODE key")
ui.write(e.EV_KEY, e.KEY_BACKSLASH, 0)
elif keycode == 4 and 35 in pressed:
logging.info(f"Released {keycode} but actually release e.KEY_LEFTSHIFT + e.KEY_BACKSLASH instead due to CODE key")
ui.write(e.EV_KEY, e.KEY_BACKSLASH, 0)
ui.write(e.EV_KEY, e.KEY_LEFTSHIFT, 0)
elif keycode == 5 and 35 in pressed:
logging.info(f"Released {keycode} but actually release e.KEY_LEFTSHIFT + e.KEY_LEFTBRACE instead due to CODE key")
ui.write(e.EV_KEY, e.KEY_LEFTBRACE, 0)
ui.write(e.EV_KEY, e.KEY_LEFTSHIFT, 0)
elif keycode == 14 and 35 in pressed:
logging.info(f"Released {keycode} but actually release e.KEY_LEFTSHIFT + e.KEY_RIGHTBRACE instead due to CODE key")
ui.write(e.EV_KEY, e.KEY_RIGHTBRACE, 0)
ui.write(e.EV_KEY, e.KEY_LEFTSHIFT, 0)
else:
# Check for num-lock being set; if so use numlock_keymap
if num_lock:
logging.info(f"Released {keycode} which is num-locked key {e.KEY[numlock_keymap[keycode]]}")
ui.write(e.EV_KEY, numlock_keymap[keycode], 0)
# Otherwise use regular keymap
else:
logging.info(f"Released {keycode} which is {e.KEY[keymap[keycode]]}")
ui.write(e.EV_KEY, keymap[keycode], 0)
# If CODE was released while another is still being held down:
# 1. Release this extra key to prevent continuous key repeat
# 2. Release the shift key
if keycode == 35 and len(pressed)>1:
logging.info(f"Also releasing {coded_key} and SHIFT")
ui.write(e.EV_KEY, coded_key, 0)
ui.write(e.EV_KEY, e.KEY_LEFTSHIFT, 0)
# If SHIFT was released while another is still being held down, also release this extra key to prevent continuous key repeat
if keycode == 8 and len(pressed)>1:
logging.info(f"Also releasing {shifted_key}")
ui.write(e.EV_KEY, shifted_key, 0)
# Remove it from the set
pressed.discard(keycode)
syn = True
GPIO.output(rows[i], GPIO.LOW)
if syn:
ui.syn()
polls_since_press = 0
sleep_time = 1/60
else:
polls_since_press = polls_since_press + 1
if polls_since_press == 600:
logging.info(f"Reducing polling rate")
sleep_time = 1/10
elif polls_since_press == 1200:
logging.info(f"Reducing polling rate again")
sleep_time = 1/5