Skip to content

Commit 57b2edf

Browse files
authored
Merge pull request #2 from Neradoc/patch-common-layout-class
switch to a common layout class, add layouts
2 parents e60f562 + 39432cb commit 57b2edf

File tree

7 files changed

+871
-443
lines changed

7 files changed

+871
-443
lines changed

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ circup install keyboard_layout_win_fr
1212

1313
`keyboard_layout_<platform>_<lang>` modules will contain the layout information for use with the `Keyboard` to type text.
1414

15+
They require also installing the `keyboard_layout.py` file, containing the base class and methods that are used by the layout. This file should be part of adafruit_hid in the future and will be removed.
16+
1517
```py
1618
import usb_hid
1719
from adafruit_hid.keyboard import Keyboard
@@ -36,4 +38,4 @@ kbd.send(Keycode.COMMAND, Keycode.A)
3638

3739
### NOTE
3840

39-
This is pretty much experimental for now, the layout and keycode files are stand-ins.
41+
A few layouts and keycodes are currently implemented, they are not thouroughly tested. The `keycode_mac_fr.py` file is more experimental.

build.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,11 @@ def make_bundle_files():
134134
shutil.copytree(MODULES_DIR, fmt(BUNDLE_LIB_DIR))
135135

136136
# list of the modules
137-
all_modules = [mod.replace(".py", "") for mod in os.listdir(MODULES_DIR)]
137+
all_modules = [
138+
mod.replace(".py", "")
139+
for mod in os.listdir(MODULES_DIR)
140+
if not mod.startswith(".")
141+
]
138142

139143
json_data = {}
140144

@@ -155,6 +159,11 @@ def make_bundle_files():
155159
"dependencies": [], # "adafruit_hid"
156160
"external_dependencies": ["adafruit-circuitpython-hid"],
157161
}
162+
# add the dependency to keyboard_layout
163+
if module.startswith("keyboard_layout_"):
164+
json_data[module]["dependencies"].append("keyboard_layout")
165+
with open(target,"a") as fp:
166+
fp.write("\r\nkeyboard_layout\r\n")
158167

159168
# create the json file
160169
with open(BUNDLE_JSON, "w") as out_file:

libraries/keyboard_layout.py

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

0 commit comments

Comments
 (0)