8000 Act gracefully when more than 6 keys are reported at once by jepler · Pull Request #103 · adafruit/Adafruit_CircuitPython_HID · GitHub
[go: up one dir, main page]

Skip to content

Act gracefully when more than 6 keys are reported at once #103

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Sep 6, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 29 additions & 18 deletions adafruit_hid/keyboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,12 @@

import time
from micropython import const
import usb_hid

from .keycode import Keycode

from . import find_device

try:
from typing import Sequence
import usb_hid
except ImportError:
pass

_MAX_KEYPRESSES = const(6)

Expand All @@ -39,7 +35,7 @@ class Keyboard:

# No more than _MAX_KEYPRESSES regular keys may be pressed at once.

def __init__(self, devices: Sequence[usb_hid.Device]) -> None:
def __init__(self, devices: list[usb_hid.Device]) -> None:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jepler Just a heads up that using built-ins as typing generics wasn't added until Python 3.9.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what's the impact of this, unimportable on older python now? what's our baseline python requirement, given that current raspberry pi os is on 3.9 by now?

Copy link
Member
@tekktrik tekktrik Sep 6, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I believe it makes it unimportable. My understanding is that the baseline is 3.7. You could get around this by typing it simply as list if you want to avoid List[usb_hid.Device], though you lose the helpfulness of saying what it should contain using the type hint. To be fair, though, I think the context of what it should contain is pretty clear between what everything is named and the docstring.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@makermelissa did I get that right?

"""Create a Keyboard object that will send keyboard HID reports.

Devices can be a sequence of devices that includes a keyboard device or a keyboard device
Expand Down Expand Up @@ -133,19 +129,22 @@ def _add_keycode_to_report(self, keycode: int) -> None:
# Set bit for this modifier.
self.report_modifier[0] |= modifier
else:
report_keys = self.report_keys
# Don't press twice.
# (I'd like to use 'not in self.report_keys' here, but that's not implemented.)
for i in range(_MAX_KEYPRESSES):
if self.report_keys[i] == keycode:
# Already pressed.
report_key = report_keys[i]
if report_key == 0:
# Put keycode in first empty slot. Since the report_keys
# are compact and unique, this is not a repeated key
report_keys[i] = keycode
return
# Put keycode in first empty slot.
for i in range(_MAX_KEYPRESSES):
if self.report_keys[i] == 0:
self.report_keys[i] = keycode
if report_key == keycode:
# Already pressed.
return
# All slots are filled.
raise ValueError("Trying to press more than six keys at once.")
# All slots are filled. Shuffle down and reuse last slot
for i in range(_MAX_KEYPRESSES - 1):
report_keys[i] = report_keys[i + 1]
report_keys[-1] = keycode

def _remove_keycode_from_report(self, keycode: int) -> None:
"""Remove a single keycode from the report."""
Expand All @@ -154,10 +153,22 @@ def _remove_keycode_from_report(self, keycode: int) -> None:
# Turn off the bit for this modifier.
self.report_modifier[0] &= ~modifier
else:
# Check all the slots, just in case there's a duplicate. (There should not be.)
report_keys = self.report_keys
# Clear the at most one matching slot and move remaining keys down
j = 0
for i in range(_MAX_KEYPRESSES):
if self.report_keys[i] == keycode:
self.report_keys[i] = 0
pressed = report_keys[i]
if not pressed:
break # Handled all used report slots
if pressed == keycode:
continue # Remove this entry
if i != j:
report_keys[j] = report_keys[i]
j += 1
# Clear any remaining slots
while j < _MAX_KEYPRESSES and report_keys[j]:
report_keys[j] = 0
j += 1

@property
def led_status(self) -> bytes:
Expand Down
0