Skip to content

Commit 45ee7e0

Browse files
committed
add keyboard_layout_base and switch keyboard_layout_us
1 parent c3c0591 commit 45ee7e0

File tree

2 files changed

+133
-76
lines changed

2 files changed

+133
-76
lines changed

adafruit_hid/keyboard_layout_base.py

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
# SPDX-FileCopyrightText: 2017 Dan Halbert for Adafruit Industries
2+
#
3+
# SPDX-License-Identifier: MIT
4+
5+
"""
6+
* Author(s): Dan Halbert, AngainorDev, Neradoc
7+
"""
8+
9+
10+
__version__ = "2.0.0-auto.0"
11+
__repo__ = "https://github.com/Neradoc/Circuitpython_Keyboard_Layouts.git"
12+
13+
14+
class KeyboardLayoutBase:
15+
"""Map ASCII characters to appropriate keypresses on a standard US PC keyboard.
16+
17+
Non-ASCII characters and most control characters will raise an exception.
18+
"""
19+
20+
# We use the top bit of each byte (0x80) to indicate
21+
# that the shift key should be pressed
22+
SHIFT_FLAG = 0x80
23+
ALTGR_FLAG = 0x80
24+
SHIFT_CODE = 0xE1
25+
RIGHT_ALT_CODE = 0xE6
26+
ASCII_TO_KEYCODE = ()
27+
NEED_ALTGR = ""
28+
HIGHER_ASCII = {}
29+
COMBINED_KEYS = {}
30+
31+
def __init__(self, keyboard):
32+
"""Specify the layout for the given keyboard.
33+
34+
:param keyboard: a Keyboard object. Write characters to this keyboard when requested.
35+
"""
36+
self.keyboard = keyboard
37+
38+
def _write(self, char, keycode, altgr=False):
39+
if keycode == 0:
40+
raise ValueError(
41+
"No keycode available for character {letter} ({num}/0x{num:02x}).".format(
42+
letter=repr(char), num=ord(char)
43+
)
44+
)
45+
if altgr:
46+
# Add altgr modifier
47+
self.keyboard.press(self.RIGHT_ALT_CODE)
48+
# If this is a shifted char, clear the SHIFT flag and press the SHIFT key.
49+
if keycode & self.SHIFT_FLAG:
50+
keycode &= ~self.SHIFT_FLAG
51+
self.keyboard.press(self.SHIFT_CODE)
52+
self.keyboard.press(keycode)
53+
self.keyboard.release_all()
54+
55+
def write(self, string):
56+
"""Type the string by pressing and releasing keys on my keyboard.
57+
58+
:param string: A string of ASCII characters.
59+
:raises ValueError: if any of the characters are not ASCII or have no keycode
60+
(such as some control characters).
61+
"""
62+
for char in string:
63+
# find easy ones first
64+
keycode = self._char_to_keycode(char)
65+
if keycode > 0:
66+
self._write(char, keycode, char in self.NEED_ALTGR)
67+
# find combined keys
68+
elif char in self.COMBINED_KEYS:
69+
cchar = self.COMBINED_KEYS[char]
70+
self._write(char, (cchar >> 8), (cchar & 0xFF & self.ALTGR_FLAG))
71+
char = chr(cchar & 0xFF & (~self.ALTGR_FLAG))
72+
keycode = self._char_to_keycode(char)
73+
# assume no altgr needed for second key
74+
self._write(char, keycode, False)
75+
else:
76+
raise ValueError(
77+
"No keycode available for character {letter} ({num}/0x{num:02x}).".format(
78+
letter=repr(char), num=ord(char)
79+
)
80+
)
81+
82+
def keycodes(self, char):
83+
"""Return a tuple of keycodes needed to type the given character.
84+
85+
:param char: A single ASCII character in a string.
86+
:type char: str of length one.
87+
:returns: tuple of Keycode keycodes.
88+
:raises ValueError: if ``char`` is not ASCII or there is no keycode for it.
89+
"""
90+
keycode = self._char_to_keycode(char)
91+
if keycode == 0:
92+
return []
93+
94+
codes = []
95+
if char in self.NEED_ALTGR:
96+
codes.append(self.RIGHT_ALT_CODE)
97+
if keycode & self.SHIFT_FLAG:
98+
codes.extend((self.SHIFT_CODE, keycode & ~self.SHIFT_FLAG))
99+
else:
100+
codes.append(keycode)
101+
102+
return codes
103+
104+
def _above128char_to_keycode(self, char):
105+
"""Return keycode for above 128 ascii codes.
106+
107+
Since the values are sparse, this may be more space efficient than bloating the table above
108+
or adding a dict.
109+
110+
:param char_val: ascii char value
111+
:return: keycode, with modifiers if needed
112+
"""
113+
if char in self.HIGHER_ASCII:
114+
return self.HIGHER_ASCII[char]
115+
if ord(char) in self.HIGHER_ASCII:
116+
return self.HIGHER_ASCII[ord(char)]
117+
return 0
118+
119+
def _char_to_keycode(self, char):
120+
"""Return the HID keycode for the given ASCII character, with the SHIFT_FLAG possibly set.
121+
122+
If the character requires pressing the Shift key, the SHIFT_FLAG bit is set.
123+
You must clear this bit before passing the keycode in a USB report.
124+
"""
125+
char_val = ord(char)
126+
if char_val > len(self.ASCII_TO_KEYCODE):
127+
return self._above128char_to_keycode(char)
128+
keycode = self.ASCII_TO_KEYCODE[char_val]
129+
return keycode

adafruit_hid/keyboard_layout_us.py

100644100755
Lines changed: 4 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,16 @@
99
* Author(s): Dan Halbert
1010
"""
1111

12-
from .keycode import Keycode
12+
from .keyboard_layout_base import KeyboardLayoutBase
1313

1414
try:
15-
from .keyboard import Keyboard
1615
from typing import Tuple
16+
from .keyboard import Keyboard
1717
except ImportError:
1818
pass
1919

2020

21-
class KeyboardLayoutUS:
21+
class KeyboardLayoutUS(KeyboardLayoutBase):
2222
"""Map ASCII characters to appropriate keypresses on a standard US PC keyboard.
2323
2424
Non-ASCII characters and most control characters will raise an exception.
@@ -37,7 +37,6 @@ class KeyboardLayoutUS:
3737
# if it's in a .mpy file, so it doesn't use up valuable RAM.
3838
#
3939
# \x00 entries have no keyboard key and so won't be sent.
40-
SHIFT_FLAG = 0x80
4140
ASCII_TO_KEYCODE = (
4241
b"\x00" # NUL
4342
b"\x00" # SOH
@@ -169,75 +168,4 @@ class KeyboardLayoutUS:
169168
b"\x4c" # DEL DELETE (called Forward Delete in usb.org document)
170169
)
171170

172-
def __init__(self, keyboard: Keyboard) -> None:
173-
"""Specify the layout for the given keyboard.
174-
175-
:param keyboard: a Keyboard object. Write characters to this keyboard when requested.
176-
177-
Example::
178-
179-
kbd = Keyboard(usb_hid.devices)
180-
layout = KeyboardLayoutUS(kbd)
181-
"""
182-
183-
self.keyboard = keyboard
184-
185-
def write(self, string: str) -> None:
186-
"""Type the string by pressing and releasing keys on my keyboard.
187-
188-
:param string: A string of ASCII characters.
189-
:raises ValueError: if any of the characters are not ASCII or have no keycode
190-
(such as some control characters).
191-
192-
Example::
193-
194-
# Write abc followed by Enter to the keyboard
195-
layout.write('abc\\n')
196-
"""
197-
for char in string:
198-
keycode = self._char_to_keycode(char)
199-
# If this is a shifted char, clear the SHIFT flag and press the SHIFT key.
200-
if keycode & self.SHIFT_FLAG:
201-
keycode &= ~self.SHIFT_FLAG
202-
self.keyboard.press(Keycode.SHIFT)
203-
self.keyboard.press(keycode)
204-
self.keyboard.release_all()
205-
206-
def keycodes(self, char: str) -> Tuple[int, ...]:
207-
"""Return a tuple of keycodes needed to type the given character.
208-
209-
:param char: A single ASCII character in a string.
210-
:type char: str of length one.
211-
:returns: tuple of Keycode keycodes.
212-
:raises ValueError: if ``char`` is not ASCII or there is no keycode for it.
213-
214-
Examples::
215-
216-
# Returns (Keycode.TAB,)
217-
keycodes('\t')
218-
# Returns (Keycode.A,)
219-
keycode('a')
220-
# Returns (Keycode.SHIFT, Keycode.A)
221-
keycode('A')
222-
# Raises ValueError because it's a accented e and is not ASCII
223-
keycode('é')
224-
"""
225-
keycode = self._char_to_keycode(char)
226-
if keycode & self.SHIFT_FLAG:
227-
return (Keycode.SHIFT, keycode & ~self.SHIFT_FLAG)
228-
229-
return (keycode,)
230-
231-
def _char_to_keycode(self, char: str) -> int:
232-
"""Return the HID keycode for the given ASCII character, with the SHIFT_FLAG possibly set.
233-
234-
If the character requires pressing the Shift key, the SHIFT_FLAG bit is set.
235-
You must clear this bit before passing the keycode in a USB report.
236-
"""
237-
char_val = ord(char)
238-
if char_val > 128:
239-
raise ValueError("Not an ASCII character.")
240-
keycode = self.ASCII_TO_KEYCODE[char_val]
241-
if keycode == 0:
242-
raise ValueError("No keycode available for character.")
243-
return keycode
171+
KeyboardLayout = KeyboardLayoutUS

0 commit comments

Comments
 (0)