HID: Support Consumer (media) keys
What a short title for such a massive diff. This (heavily squashed) commit adds support for Consumer keys such as volume keys, media play/pause/stop, etc. by exposing four HID devices over a single USB lane (as opposed to just exposing a keyboard). This heavily refactors how HIDHelper works due to the new reporting structure. Many of the media keys were changed (mostly Keycodes.Media section), but many (especially anything regarding Application keys) haven't been touched yet - thus many keycodes may still be wrong. Probably worth updating those soon, but I didn't get around to it yet. The definitive list I refered to was http://www.freebsddiary.org/APC/usb_hid_usages.php, which is basically copy-pasta from the official USB HID spec at https://www.usb.org/sites/default/files/documents/hut1_12v2.pdf (warning: massive PDF, not light reading). The only known regression this introduces is that instead of 6KRO as the USB spec usually supports, we can now only have 5KRO (maybe even 4KRO), for reasons I have yet to fully debug - this seems to be related to the report having to include the device descriptor _and_ not supporting a full 8 bytes as it used to. For now I'm willing to accept this, but it definitely will be great to squash that bug. This adds descriptor support for MOUSE and SYSCONTROL devices, as of yet unimplemented.
This commit is contained in:
parent
a1bfaac691
commit
b7b1866ac9
@ -1,3 +1,109 @@
|
||||
class HIDReportTypes:
|
||||
KEYBOARD = 1
|
||||
MOUSE = 2
|
||||
CONSUMER = 3
|
||||
SYSCONTROL = 4
|
||||
|
||||
|
||||
HID_REPORT_STRUCTURE = bytes([
|
||||
# Regular keyboard
|
||||
0x05, 0x01, # Usage Page (Generic Desktop)
|
||||
0x09, 0x06, # Usage (Keyboard)
|
||||
0xA1, 0x01, # Collection (Application)
|
||||
0x85, HIDReportTypes.KEYBOARD, # Report ID (1)
|
||||
0x05, 0x07, # Usage Page (Keyboard)
|
||||
0x19, 224, # Usage Minimum (224)
|
||||
0x29, 231, # Usage Maximum (231)
|
||||
0x15, 0x00, # Logical Minimum (0)
|
||||
0x25, 0x01, # Logical Maximum (1)
|
||||
0x75, 0x01, # Report Size (1)
|
||||
0x95, 0x08, # Report Count (8)
|
||||
0x81, 0x02, # Input (Data, Variable, Absolute)
|
||||
0x81, 0x01, # Input (Constant)
|
||||
0x19, 0x00, # Usage Minimum (0)
|
||||
0x29, 101, # Usage Maximum (101)
|
||||
0x15, 0x00, # Logical Minimum (0)
|
||||
0x25, 101, # Logical Maximum (101)
|
||||
0x75, 0x08, # Report Size (8)
|
||||
0x95, 0x06, # Report Count (6)
|
||||
0x81, 0x00, # Input (Data, Array)
|
||||
0x05, 0x08, # Usage Page (LED)
|
||||
0x19, 0x01, # Usage Minimum (1)
|
||||
0x29, 0x05, # Usage Maximum (5)
|
||||
0x15, 0x00, # Logical Minimum (0)
|
||||
0x25, 0x01, # Logical Maximum (1)
|
||||
0x75, 0x01, # Report Size (1)
|
||||
0x95, 0x05, # Report Count (5)
|
||||
0x91, 0x02, # Output (Data, Variable, Absolute)
|
||||
0x95, 0x03, # Report Count (3)
|
||||
0x91, 0x01, # Output (Constant)
|
||||
0xC0, # End Collection
|
||||
# Regular mouse
|
||||
0x05, 0x01, # Usage Page (Generic Desktop)
|
||||
0x09, 0x02, # Usage (Mouse)
|
||||
0xA1, 0x01, # Collection (Application)
|
||||
0x09, 0x01, # Usage (Pointer)
|
||||
0xA1, 0x00, # Collection (Physical)
|
||||
0x85, HIDReportTypes.MOUSE, # Report ID (n)
|
||||
0x05, 0x09, # Usage Page (Button)
|
||||
0x19, 0x01, # Usage Minimum (0x01)
|
||||
0x29, 0x05, # Usage Maximum (0x05)
|
||||
0x15, 0x00, # Logical Minimum (0)
|
||||
0x25, 0x01, # Logical Maximum (1)
|
||||
0x95, 0x05, # Report Count (5)
|
||||
0x75, 0x01, # Report Size (1)
|
||||
0x81, 0x02, # Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0x95, 0x01, # Report Count (1)
|
||||
0x75, 0x03, # Report Size (3)
|
||||
0x81, 0x01, # Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0x05, 0x01, # Usage Page (Generic Desktop Ctrls)
|
||||
0x09, 0x30, # Usage (X)
|
||||
0x09, 0x31, # Usage (Y)
|
||||
0x15, 0x81, # Logical Minimum (-127)
|
||||
0x25, 0x7F, # Logical Maximum (127)
|
||||
0x75, 0x08, # Report Size (8)
|
||||
0x95, 0x02, # Report Count (2)
|
||||
0x81, 0x06, # Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0x09, 0x38, # Usage (Wheel)
|
||||
0x15, 0x81, # Logical Minimum (-127)
|
||||
0x25, 0x7F, # Logical Maximum (127)
|
||||
0x75, 0x08, # Report Size (8)
|
||||
0x95, 0x01, # Report Count (1)
|
||||
0x81, 0x06, # Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0xC0, # End Collection
|
||||
0xC0, # End Collection
|
||||
# Consumer ("multimedia") keys
|
||||
0x05, 0x0C, # Usage Page (Consumer)
|
||||
0x09, 0x01, # Usage (Consumer Control)
|
||||
0xA1, 0x01, # Collection (Application)
|
||||
0x85, HIDReportTypes.CONSUMER, # Report ID (n)
|
||||
0x75, 0x10, # Report Size (16)
|
||||
0x95, 0x01, # Report Count (1)
|
||||
0x15, 0x01, # Logical Minimum (1)
|
||||
0x26, 0x8C, 0x02, # Logical Maximum (652)
|
||||
0x19, 0x01, # Usage Minimum (Consumer Control)
|
||||
0x2A, 0x8C, 0x02, # Usage Maximum (AC Send)
|
||||
0x81, 0x00, # Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0xC0, # End Collection
|
||||
# Power controls
|
||||
0x05, 0x01, # Usage Page (Generic Desktop Ctrls)
|
||||
0x09, 0x80, # Usage (Sys Control)
|
||||
0xA1, 0x01, # Collection (Application)
|
||||
0x85, HIDReportTypes.SYSCONTROL, # Report ID (n)
|
||||
0x75, 0x02, # Report Size (2)
|
||||
0x95, 0x01, # Report Count (1)
|
||||
0x15, 0x01, # Logical Minimum (1)
|
||||
0x25, 0x03, # Logical Maximum (3)
|
||||
0x09, 0x82, # Usage (Sys Sleep)
|
||||
0x09, 0x81, # Usage (Sys Power Down)
|
||||
0x09, 0x83, # Usage (Sys Wake Up)
|
||||
0x81, 0x60, # Input (Data,Array,Abs,No Wrap,Linear,No Preferred State,Null State)
|
||||
0x75, 0x06, # Report Size (6)
|
||||
0x81, 0x03, # Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0xC0, # End Collection
|
||||
])
|
||||
|
||||
|
||||
class DiodeOrientation:
|
||||
'''
|
||||
Orientation of diodes on handwired boards. You can think of:
|
||||
|
@ -24,6 +24,11 @@ class ModifierKeycode:
|
||||
self.code = code
|
||||
|
||||
|
||||
class ConsumerKeycode:
|
||||
def __init__(self, code):
|
||||
self.code = code
|
||||
|
||||
|
||||
class KeycodeCategory(type):
|
||||
@classmethod
|
||||
def to_dict(cls):
|
||||
@ -121,6 +126,9 @@ CODE_RGUI = CODE_RCMD = CODE_RWIN = 0x80
|
||||
class Keycodes(KeycodeCategory):
|
||||
'''
|
||||
A massive grouping of keycodes
|
||||
|
||||
Some of these are from http://www.freebsddiary.org/APC/usb_hid_usages.php,
|
||||
one of the most useful pages on the interwebs for HID stuff, apparently.
|
||||
'''
|
||||
class Modifiers(KeycodeCategory):
|
||||
KC_LCTRL = KC_LCTL = ModifierKeycode(CODE_LCTRL)
|
||||
@ -280,60 +288,64 @@ class Keycodes(KeycodeCategory):
|
||||
KC_LANG9 = Keycode(152)
|
||||
|
||||
class Misc(KeycodeCategory):
|
||||
KC_APPLICATION = KC_APP = Keycode(101)
|
||||
KC_POWER = Keycode(102)
|
||||
KC_EXECUTE = KC_EXEC = Keycode(116)
|
||||
KC_SYSTEM_POWER = KC_PWR = Keycode(165)
|
||||
KC_SYSTEM_SLEEP = KC_SLEP = Keycode(166)
|
||||
KC_SYSTEM_WAKE = KC_WAKE = Keycode(167)
|
||||
KC_HELP = Keycode(117)
|
||||
KC_MENU = Keycode(118)
|
||||
KC_SELECT = KC_SLCT = Keycode(119)
|
||||
KC_STOP = Keycode(120)
|
||||
KC_AGAIN = KC_AGIN = Keycode(121)
|
||||
KC_UNDO = Keycode(122)
|
||||
KC_CUT = Keycode(123)
|
||||
KC_COPY = Keycode(124)
|
||||
KC_PASTE = KC_PSTE = Keycode(125)
|
||||
KC_FIND = Keycode(126)
|
||||
KC_ALT_ERASE = KC_ERAS = Keycode(153)
|
||||
KC_SYSREQ = Keycode(154)
|
||||
KC_CANCEL = Keycode(155)
|
||||
KC_CLEAR = KC_CLR = Keycode(156)
|
||||
KC_PRIOR = Keycode(157)
|
||||
KC_RETURN = Keycode(158)
|
||||
KC_SEPERATOR = Keycode(159)
|
||||
KC_OUT = Keycode(160)
|
||||
KC_OPER = Keycode(161)
|
||||
KC_CLEAR_AGAIN = Keycode(162)
|
||||
KC_CRSEL = Keycode(163)
|
||||
KC_EXSEL = Keycode(164)
|
||||
KC_MAIL = Keycode(177)
|
||||
KC_CALCULATOR = KC_CALC = Keycode(178)
|
||||
KC_MY_COMPUTER = KC_MYCM = Keycode(179)
|
||||
KC_WWW_SEARCH = KC_WSCH = Keycode(180)
|
||||
KC_WWW_HOME = KC_WHOM = Keycode(181)
|
||||
KC_WWW_BACK = KC_WBAK = Keycode(182)
|
||||
KC_WWW_FORWARD = KC_WFWD = Keycode(183)
|
||||
KC_WWW_STOP = KC_WSTP = Keycode(184)
|
||||
KC_WWW_REFRESH = KC_WREF = Keycode(185)
|
||||
KC_WWW_FAVORITES = KC_WFAV = Keycode(186)
|
||||
KC_APPLICATION = KC_APP = ConsumerKeycode(101)
|
||||
KC_POWER = ConsumerKeycode(102)
|
||||
KC_EXECUTE = KC_EXEC = ConsumerKeycode(116)
|
||||
KC_SYSTEM_POWER = KC_PWR = ConsumerKeycode(165)
|
||||
KC_SYSTEM_SLEEP = KC_SLEP = ConsumerKeycode(166)
|
||||
KC_SYSTEM_WAKE = KC_WAKE = ConsumerKeycode(167)
|
||||
KC_HELP = ConsumerKeycode(117)
|
||||
KC_MENU = ConsumerKeycode(118)
|
||||
KC_SELECT = KC_SLCT = ConsumerKeycode(119)
|
||||
KC_STOP = ConsumerKeycode(120)
|
||||
KC_AGAIN = KC_AGIN = ConsumerKeycode(121)
|
||||
KC_UNDO = ConsumerKeycode(122)
|
||||
KC_CUT = ConsumerKeycode(123)
|
||||
KC_COPY = ConsumerKeycode(124)
|
||||
KC_PASTE = KC_PSTE = ConsumerKeycode(125)
|
||||
KC_FIND = ConsumerKeycode(126)
|
||||
KC_ALT_ERASE = KC_ERAS = ConsumerKeycode(153)
|
||||
KC_SYSREQ = ConsumerKeycode(154)
|
||||
KC_CANCEL = ConsumerKeycode(155)
|
||||
KC_CLEAR = KC_CLR = ConsumerKeycode(156)
|
||||
KC_PRIOR = ConsumerKeycode(157)
|
||||
KC_RETURN = ConsumerKeycode(158)
|
||||
KC_SEPERATOR = ConsumerKeycode(159)
|
||||
KC_OUT = ConsumerKeycode(160)
|
||||
KC_OPER = ConsumerKeycode(161)
|
||||
KC_CLEAR_AGAIN = ConsumerKeycode(162)
|
||||
KC_CRSEL = ConsumerKeycode(163)
|
||||
KC_EXSEL = ConsumerKeycode(164)
|
||||
KC_MAIL = ConsumerKeycode(177)
|
||||
KC_CALCULATOR = KC_CALC = ConsumerKeycode(178)
|
||||
KC_MY_COMPUTER = KC_MYCM = ConsumerKeycode(179)
|
||||
KC_WWW_SEARCH = KC_WSCH = ConsumerKeycode(180)
|
||||
KC_WWW_HOME = KC_WHOM = ConsumerKeycode(181)
|
||||
KC_WWW_BACK = KC_WBAK = ConsumerKeycode(182)
|
||||
KC_WWW_FORWARD = KC_WFWD = ConsumerKeycode(183)
|
||||
KC_WWW_STOP = KC_WSTP = ConsumerKeycode(184)
|
||||
KC_WWW_REFRESH = KC_WREF = ConsumerKeycode(185)
|
||||
KC_WWW_FAVORITES = KC_WFAV = ConsumerKeycode(186)
|
||||
|
||||
class Media(KeycodeCategory):
|
||||
KC__MUTE = Keycode(127)
|
||||
KC__VOLUP = Keycode(128)
|
||||
KC__VOLDOWN = Keycode(129)
|
||||
KC_AUDIO_MUTE = KC_MUTE = Keycode(168)
|
||||
KC_AUDIO_VOL_UP = KC_VOLU = Keycode(169)
|
||||
KC_AUDIO_VOL_DOWN = KC_VOLD = Keycode(170)
|
||||
KC_MEDIA_NEXT_TRACK = KC_MNXT = Keycode(171)
|
||||
KC_MEDIA_PREV_TRACK = KC_MPRV = Keycode(172)
|
||||
KC_MEDIA_STOP = KC_MSTP = Keycode(173)
|
||||
KC_MEDIA_PLAY_PAUSE = KC_MPLY = Keycode(174)
|
||||
KC_MEDIA_SELECT = KC_MSEL = Keycode(175)
|
||||
KC_MEDIA_EJECT = KC_EJCT = Keycode(176)
|
||||
KC_MEDIA_FAST_FORWARD = KC_MFFD = Keycode(187)
|
||||
KC_MEDIA_REWIND = KC_MRWD = Keycode(189)
|
||||
# I believe QMK used these double-underscore codes for MacOS
|
||||
# support or something. I have no idea, but modern MacOS supports
|
||||
# PC volume keys so I really don't care that these codes are the
|
||||
# same as below. If bugs arise, these codes may need to change.
|
||||
KC__MUTE = ConsumerKeycode(226)
|
||||
KC__VOLUP = ConsumerKeycode(233)
|
||||
KC__VOLDOWN = ConsumerKeycode(234)
|
||||
|
||||
KC_AUDIO_MUTE = KC_MUTE = ConsumerKeycode(226) # 0xE2
|
||||
KC_AUDIO_VOL_UP = KC_VOLU = ConsumerKeycode(233) # 0xE9
|
||||
KC_AUDIO_VOL_DOWN = KC_VOLD = ConsumerKeycode(234) # 0xEA
|
||||
KC_MEDIA_NEXT_TRACK = KC_MNXT = ConsumerKeycode(181) # 0xB5
|
||||
KC_MEDIA_PREV_TRACK = KC_MPRV = ConsumerKeycode(182) # 0xB6
|
||||
KC_MEDIA_STOP = KC_MSTP = ConsumerKeycode(183) # 0xB7
|
||||
KC_MEDIA_PLAY_PAUSE = KC_MPLY = ConsumerKeycode(205) # 0xCD (this may not be right)
|
||||
KC_MEDIA_EJECT = KC_EJCT = ConsumerKeycode(184) # 0xB8
|
||||
KC_MEDIA_FAST_FORWARD = KC_MFFD = ConsumerKeycode(179) # 0xB3
|
||||
KC_MEDIA_REWIND = KC_MRWD = ConsumerKeycode(180) # 0xB4
|
||||
|
||||
class KMK(KeycodeCategory):
|
||||
KC_RESET = Keycode(1000)
|
||||
|
@ -1,3 +1,6 @@
|
||||
import pyb
|
||||
|
||||
pyb.usb_mode('VCP+HID', hid=pyb.hid_keyboard) # act as a serial device and a mouse
|
||||
from kmk.micropython.pyb_hid import generate_pyb_hid_descriptor
|
||||
|
||||
# act as a serial device and a KMK device
|
||||
pyb.usb_mode('VCP+HID', hid=generate_pyb_hid_descriptor())
|
||||
|
@ -1,34 +1,22 @@
|
||||
import logging
|
||||
import string
|
||||
|
||||
from pyb import USB_HID, delay
|
||||
from pyb import USB_HID, delay, hid_keyboard
|
||||
|
||||
from kmk.common.consts import HID_REPORT_STRUCTURE, HIDReportTypes
|
||||
from kmk.common.event_defs import HID_REPORT_EVENT
|
||||
from kmk.common.keycodes import (FIRST_KMK_INTERNAL_KEYCODE, Keycodes,
|
||||
ModifierKeycode, char_lookup)
|
||||
from kmk.common.keycodes import (FIRST_KMK_INTERNAL_KEYCODE, ConsumerKeycode,
|
||||
Keycodes, ModifierKeycode, char_lookup)
|
||||
|
||||
|
||||
def generate_pyb_hid_descriptor():
|
||||
existing_keyboard = list(hid_keyboard)
|
||||
existing_keyboard[-1] = HID_REPORT_STRUCTURE
|
||||
return tuple(existing_keyboard)
|
||||
|
||||
|
||||
class HIDHelper:
|
||||
'''
|
||||
Wraps a HID reporting event. The structure of such events is (courtesy of
|
||||
http://wiki.micropython.org/USB-HID-Keyboard-mode-example-a-password-dongle):
|
||||
|
||||
>Byte 0 is for a modifier key, or combination thereof. It is used as a
|
||||
>bitmap, each bit mapped to a modifier:
|
||||
> bit 0: left control
|
||||
> bit 1: left shift
|
||||
> bit 2: left alt
|
||||
> bit 3: left GUI (Win/Apple/Meta key)
|
||||
> bit 4: right control
|
||||
> bit 5: right shift
|
||||
> bit 6: right alt
|
||||
> bit 7: right GUI
|
||||
>
|
||||
> Examples: 0x02 for Shift, 0x05 for Control+Alt
|
||||
>
|
||||
>Byte 1 is "reserved" (unused, actually)
|
||||
>Bytes 2-7 are for the actual key scancode(s) - up to 6 at a time ("chording").
|
||||
|
||||
Most methods here return `self` upon completion, allowing chaining:
|
||||
|
||||
```python
|
||||
@ -46,24 +34,64 @@ class HIDHelper:
|
||||
)
|
||||
|
||||
self._hid = USB_HID()
|
||||
self.clear_all()
|
||||
|
||||
# For some bizarre reason this can no longer be 8, it'll just fail to
|
||||
# send anything. This is almost certainly a bug in the report descriptor
|
||||
# sent over in the boot process. For now the sacrifice is that we only
|
||||
# support 5KRO until I figure this out, rather than the 6KRO HID defines.
|
||||
self._evt = bytearray(7)
|
||||
self.report_device = memoryview(self._evt)[0:1]
|
||||
|
||||
# Landmine alert for HIDReportTypes.KEYBOARD: byte index 1 of this view
|
||||
# is "reserved" and evidently (mostly?) unused. However, other modes (or
|
||||
# at least consumer, so far) will use this byte, which is the main reason
|
||||
# this view exists. For KEYBOARD, use report_mods and report_non_mods
|
||||
self.report_keys = memoryview(self._evt)[1:]
|
||||
|
||||
self.report_mods = memoryview(self._evt)[1:2]
|
||||
self.report_non_mods = memoryview(self._evt)[3:]
|
||||
|
||||
def _subscription(self, state, action):
|
||||
if action['type'] == HID_REPORT_EVENT:
|
||||
self.clear_all()
|
||||
|
||||
consumer_key = None
|
||||
for key in state.keys_pressed:
|
||||
if key.code >= FIRST_KMK_INTERNAL_KEYCODE:
|
||||
continue
|
||||
if isinstance(key, ConsumerKeycode):
|
||||
consumer_key = key
|
||||
break
|
||||
|
||||
if isinstance(key, ModifierKeycode):
|
||||
self.add_modifier(key)
|
||||
else:
|
||||
self.add_key(key)
|
||||
reporting_device = self.report_device[0]
|
||||
needed_reporting_device = HIDReportTypes.KEYBOARD
|
||||
|
||||
if key.has_modifiers:
|
||||
for mod in key.has_modifiers:
|
||||
self.add_modifier(mod)
|
||||
if consumer_key:
|
||||
needed_reporting_device = HIDReportTypes.CONSUMER
|
||||
|
||||
if reporting_device != needed_reporting_device:
|
||||
# If we are about to change reporting devices, release
|
||||
# all keys and close our proverbial tab on the existing
|
||||
# device, or keys will get stuck (mostly when releasing
|
||||
# media/consumer keys)
|
||||
self.send()
|
||||
delay(10)
|
||||
|
||||
self.report_device[0] = needed_reporting_device
|
||||
|
||||
if consumer_key:
|
||||
self.add_key(consumer_key)
|
||||
else:
|
||||
for key in state.keys_pressed:
|
||||
if key.code >= FIRST_KMK_INTERNAL_KEYCODE:
|
||||
continue
|
||||
|
||||
if isinstance(key, ModifierKeycode):
|
||||
self.add_modifier(key)
|
||||
else:
|
||||
self.add_key(key)
|
||||
|
||||
if key.has_modifiers:
|
||||
for mod in key.has_modifiers:
|
||||
self.add_modifier(mod)
|
||||
|
||||
self.send()
|
||||
|
||||
@ -114,37 +142,45 @@ class HIDHelper:
|
||||
return self
|
||||
|
||||
def clear_all(self):
|
||||
self._evt = bytearray(8)
|
||||
for idx, _ in enumerate(self.report_keys):
|
||||
self.report_keys[idx] = 0x00
|
||||
|
||||
return self
|
||||
|
||||
def clear_non_modifiers(self):
|
||||
for pos in range(2, 8):
|
||||
self._evt[pos] = 0x00
|
||||
for idx, _ in enumerate(self.report_non_mods):
|
||||
self.report_non_mods[idx] = 0x00
|
||||
|
||||
return self
|
||||
|
||||
def add_modifier(self, modifier):
|
||||
if isinstance(modifier, ModifierKeycode):
|
||||
self._evt[0] |= modifier.code
|
||||
self.report_mods[0] |= modifier.code
|
||||
else:
|
||||
self._evt[0] |= modifier
|
||||
self.report_mods[0] |= modifier
|
||||
|
||||
return self
|
||||
|
||||
def remove_modifier(self, modifier):
|
||||
if isinstance(modifier, ModifierKeycode):
|
||||
self._evt[0] ^= modifier.code
|
||||
self.report_mods[0] ^= modifier.code
|
||||
else:
|
||||
self._evt[0] ^= modifier
|
||||
self.report_mods[0] ^= modifier
|
||||
|
||||
return self
|
||||
|
||||
def add_key(self, key):
|
||||
# Try to find the first empty slot in the key report, and fill it
|
||||
placed = False
|
||||
for pos in range(2, 8):
|
||||
if self._evt[pos] == 0x00:
|
||||
self._evt[pos] = key.code
|
||||
|
||||
where_to_place = self.report_non_mods
|
||||
|
||||
if self.report_device[0] == HIDReportTypes.CONSUMER:
|
||||
where_to_place = self.report_keys
|
||||
|
||||
for idx, _ in enumerate(where_to_place):
|
||||
if where_to_place[idx] == 0x00:
|
||||
where_to_place[idx] = key.code
|
||||
placed = True
|
||||
break
|
||||
|
||||
@ -155,9 +191,15 @@ class HIDHelper:
|
||||
|
||||
def remove_key(self, key):
|
||||
removed = False
|
||||
for pos in range(2, 8):
|
||||
if self._evt[pos] == key.code:
|
||||
self._evt[pos] = 0x00
|
||||
|
||||
where_to_place = self.report_non_mods
|
||||
|
||||
if self.report_device[0] == HIDReportTypes.CONSUMER:
|
||||
where_to_place = self.report_keys
|
||||
|
||||
for idx, _ in enumerate(where_to_place):
|
||||
if where_to_place[idx] == key.code:
|
||||
where_to_place[idx] = 0x00
|
||||
removed = True
|
||||
|
||||
if not removed:
|
||||
|
@ -1,7 +1,7 @@
|
||||
[flake8]
|
||||
exclude = .git,__pycache__,vendor,.venv
|
||||
max_line_length = 99
|
||||
ignore = X100
|
||||
ignore = X100, E262
|
||||
per-file-ignores =
|
||||
user_keymaps/**/*.py: F401,E501
|
||||
tests/test_data/keymaps/**/*.py: F401,E501
|
||||
|
@ -22,8 +22,8 @@ keymap = [
|
||||
[KC.F, KC.G, KC.H],
|
||||
],
|
||||
[
|
||||
[KC.X, KC.Y, KC.Z],
|
||||
[KC.TRNS, KC.PIPE, KC.O],
|
||||
[KC.R, KC.P, KC.Q],
|
||||
[KC.VOLU, KC.MUTE, KC.Z],
|
||||
[KC.TRNS, KC.PIPE, KC.MEDIA_PLAY_PAUSE],
|
||||
[KC.VOLD, KC.P, KC.Q],
|
||||
],
|
||||
]
|
||||
|
Loading…
Reference in New Issue
Block a user