Merge pull request #30 from KMKfw/topic-consumer-device
HID: Support Consumer (media) keys
This commit is contained in:
commit
85cdf03572
@ -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:
|
class DiodeOrientation:
|
||||||
'''
|
'''
|
||||||
Orientation of diodes on handwired boards. You can think of:
|
Orientation of diodes on handwired boards. You can think of:
|
||||||
|
@ -24,6 +24,11 @@ class ModifierKeycode:
|
|||||||
self.code = code
|
self.code = code
|
||||||
|
|
||||||
|
|
||||||
|
class ConsumerKeycode:
|
||||||
|
def __init__(self, code):
|
||||||
|
self.code = code
|
||||||
|
|
||||||
|
|
||||||
class KeycodeCategory(type):
|
class KeycodeCategory(type):
|
||||||
@classmethod
|
@classmethod
|
||||||
def to_dict(cls):
|
def to_dict(cls):
|
||||||
@ -121,6 +126,9 @@ CODE_RGUI = CODE_RCMD = CODE_RWIN = 0x80
|
|||||||
class Keycodes(KeycodeCategory):
|
class Keycodes(KeycodeCategory):
|
||||||
'''
|
'''
|
||||||
A massive grouping of keycodes
|
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):
|
class Modifiers(KeycodeCategory):
|
||||||
KC_LCTRL = KC_LCTL = ModifierKeycode(CODE_LCTRL)
|
KC_LCTRL = KC_LCTL = ModifierKeycode(CODE_LCTRL)
|
||||||
@ -280,60 +288,64 @@ class Keycodes(KeycodeCategory):
|
|||||||
KC_LANG9 = Keycode(152)
|
KC_LANG9 = Keycode(152)
|
||||||
|
|
||||||
class Misc(KeycodeCategory):
|
class Misc(KeycodeCategory):
|
||||||
KC_APPLICATION = KC_APP = Keycode(101)
|
KC_APPLICATION = KC_APP = ConsumerKeycode(101)
|
||||||
KC_POWER = Keycode(102)
|
KC_POWER = ConsumerKeycode(102)
|
||||||
KC_EXECUTE = KC_EXEC = Keycode(116)
|
KC_EXECUTE = KC_EXEC = ConsumerKeycode(116)
|
||||||
KC_SYSTEM_POWER = KC_PWR = Keycode(165)
|
KC_SYSTEM_POWER = KC_PWR = ConsumerKeycode(165)
|
||||||
KC_SYSTEM_SLEEP = KC_SLEP = Keycode(166)
|
KC_SYSTEM_SLEEP = KC_SLEP = ConsumerKeycode(166)
|
||||||
KC_SYSTEM_WAKE = KC_WAKE = Keycode(167)
|
KC_SYSTEM_WAKE = KC_WAKE = ConsumerKeycode(167)
|
||||||
KC_HELP = Keycode(117)
|
KC_HELP = ConsumerKeycode(117)
|
||||||
KC_MENU = Keycode(118)
|
KC_MENU = ConsumerKeycode(118)
|
||||||
KC_SELECT = KC_SLCT = Keycode(119)
|
KC_SELECT = KC_SLCT = ConsumerKeycode(119)
|
||||||
KC_STOP = Keycode(120)
|
KC_STOP = ConsumerKeycode(120)
|
||||||
KC_AGAIN = KC_AGIN = Keycode(121)
|
KC_AGAIN = KC_AGIN = ConsumerKeycode(121)
|
||||||
KC_UNDO = Keycode(122)
|
KC_UNDO = ConsumerKeycode(122)
|
||||||
KC_CUT = Keycode(123)
|
KC_CUT = ConsumerKeycode(123)
|
||||||
KC_COPY = Keycode(124)
|
KC_COPY = ConsumerKeycode(124)
|
||||||
KC_PASTE = KC_PSTE = Keycode(125)
|
KC_PASTE = KC_PSTE = ConsumerKeycode(125)
|
||||||
KC_FIND = Keycode(126)
|
KC_FIND = ConsumerKeycode(126)
|
||||||
KC_ALT_ERASE = KC_ERAS = Keycode(153)
|
KC_ALT_ERASE = KC_ERAS = ConsumerKeycode(153)
|
||||||
KC_SYSREQ = Keycode(154)
|
KC_SYSREQ = ConsumerKeycode(154)
|
||||||
KC_CANCEL = Keycode(155)
|
KC_CANCEL = ConsumerKeycode(155)
|
||||||
KC_CLEAR = KC_CLR = Keycode(156)
|
KC_CLEAR = KC_CLR = ConsumerKeycode(156)
|
||||||
KC_PRIOR = Keycode(157)
|
KC_PRIOR = ConsumerKeycode(157)
|
||||||
KC_RETURN = Keycode(158)
|
KC_RETURN = ConsumerKeycode(158)
|
||||||
KC_SEPERATOR = Keycode(159)
|
KC_SEPERATOR = ConsumerKeycode(159)
|
||||||
KC_OUT = Keycode(160)
|
KC_OUT = ConsumerKeycode(160)
|
||||||
KC_OPER = Keycode(161)
|
KC_OPER = ConsumerKeycode(161)
|
||||||
KC_CLEAR_AGAIN = Keycode(162)
|
KC_CLEAR_AGAIN = ConsumerKeycode(162)
|
||||||
KC_CRSEL = Keycode(163)
|
KC_CRSEL = ConsumerKeycode(163)
|
||||||
KC_EXSEL = Keycode(164)
|
KC_EXSEL = ConsumerKeycode(164)
|
||||||
KC_MAIL = Keycode(177)
|
KC_MAIL = ConsumerKeycode(177)
|
||||||
KC_CALCULATOR = KC_CALC = Keycode(178)
|
KC_CALCULATOR = KC_CALC = ConsumerKeycode(178)
|
||||||
KC_MY_COMPUTER = KC_MYCM = Keycode(179)
|
KC_MY_COMPUTER = KC_MYCM = ConsumerKeycode(179)
|
||||||
KC_WWW_SEARCH = KC_WSCH = Keycode(180)
|
KC_WWW_SEARCH = KC_WSCH = ConsumerKeycode(180)
|
||||||
KC_WWW_HOME = KC_WHOM = Keycode(181)
|
KC_WWW_HOME = KC_WHOM = ConsumerKeycode(181)
|
||||||
KC_WWW_BACK = KC_WBAK = Keycode(182)
|
KC_WWW_BACK = KC_WBAK = ConsumerKeycode(182)
|
||||||
KC_WWW_FORWARD = KC_WFWD = Keycode(183)
|
KC_WWW_FORWARD = KC_WFWD = ConsumerKeycode(183)
|
||||||
KC_WWW_STOP = KC_WSTP = Keycode(184)
|
KC_WWW_STOP = KC_WSTP = ConsumerKeycode(184)
|
||||||
KC_WWW_REFRESH = KC_WREF = Keycode(185)
|
KC_WWW_REFRESH = KC_WREF = ConsumerKeycode(185)
|
||||||
KC_WWW_FAVORITES = KC_WFAV = Keycode(186)
|
KC_WWW_FAVORITES = KC_WFAV = ConsumerKeycode(186)
|
||||||
|
|
||||||
class Media(KeycodeCategory):
|
class Media(KeycodeCategory):
|
||||||
KC__MUTE = Keycode(127)
|
# I believe QMK used these double-underscore codes for MacOS
|
||||||
KC__VOLUP = Keycode(128)
|
# support or something. I have no idea, but modern MacOS supports
|
||||||
KC__VOLDOWN = Keycode(129)
|
# PC volume keys so I really don't care that these codes are the
|
||||||
KC_AUDIO_MUTE = KC_MUTE = Keycode(168)
|
# same as below. If bugs arise, these codes may need to change.
|
||||||
KC_AUDIO_VOL_UP = KC_VOLU = Keycode(169)
|
KC__MUTE = ConsumerKeycode(226)
|
||||||
KC_AUDIO_VOL_DOWN = KC_VOLD = Keycode(170)
|
KC__VOLUP = ConsumerKeycode(233)
|
||||||
KC_MEDIA_NEXT_TRACK = KC_MNXT = Keycode(171)
|
KC__VOLDOWN = ConsumerKeycode(234)
|
||||||
KC_MEDIA_PREV_TRACK = KC_MPRV = Keycode(172)
|
|
||||||
KC_MEDIA_STOP = KC_MSTP = Keycode(173)
|
KC_AUDIO_MUTE = KC_MUTE = ConsumerKeycode(226) # 0xE2
|
||||||
KC_MEDIA_PLAY_PAUSE = KC_MPLY = Keycode(174)
|
KC_AUDIO_VOL_UP = KC_VOLU = ConsumerKeycode(233) # 0xE9
|
||||||
KC_MEDIA_SELECT = KC_MSEL = Keycode(175)
|
KC_AUDIO_VOL_DOWN = KC_VOLD = ConsumerKeycode(234) # 0xEA
|
||||||
KC_MEDIA_EJECT = KC_EJCT = Keycode(176)
|
KC_MEDIA_NEXT_TRACK = KC_MNXT = ConsumerKeycode(181) # 0xB5
|
||||||
KC_MEDIA_FAST_FORWARD = KC_MFFD = Keycode(187)
|
KC_MEDIA_PREV_TRACK = KC_MPRV = ConsumerKeycode(182) # 0xB6
|
||||||
KC_MEDIA_REWIND = KC_MRWD = Keycode(189)
|
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):
|
class KMK(KeycodeCategory):
|
||||||
KC_RESET = Keycode(1000)
|
KC_RESET = Keycode(1000)
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
import pyb
|
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 logging
|
||||||
import string
|
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.event_defs import HID_REPORT_EVENT
|
||||||
from kmk.common.keycodes import (FIRST_KMK_INTERNAL_KEYCODE, Keycodes,
|
from kmk.common.keycodes import (FIRST_KMK_INTERNAL_KEYCODE, ConsumerKeycode,
|
||||||
ModifierKeycode, char_lookup)
|
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:
|
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:
|
Most methods here return `self` upon completion, allowing chaining:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
@ -46,12 +34,52 @@ class HIDHelper:
|
|||||||
)
|
)
|
||||||
|
|
||||||
self._hid = USB_HID()
|
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):
|
def _subscription(self, state, action):
|
||||||
if action['type'] == HID_REPORT_EVENT:
|
if action['type'] == HID_REPORT_EVENT:
|
||||||
self.clear_all()
|
self.clear_all()
|
||||||
|
|
||||||
|
consumer_key = None
|
||||||
|
for key in state.keys_pressed:
|
||||||
|
if isinstance(key, ConsumerKeycode):
|
||||||
|
consumer_key = key
|
||||||
|
break
|
||||||
|
|
||||||
|
reporting_device = self.report_device[0]
|
||||||
|
needed_reporting_device = HIDReportTypes.KEYBOARD
|
||||||
|
|
||||||
|
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:
|
for key in state.keys_pressed:
|
||||||
if key.code >= FIRST_KMK_INTERNAL_KEYCODE:
|
if key.code >= FIRST_KMK_INTERNAL_KEYCODE:
|
||||||
continue
|
continue
|
||||||
@ -114,37 +142,45 @@ class HIDHelper:
|
|||||||
return self
|
return self
|
||||||
|
|
||||||
def clear_all(self):
|
def clear_all(self):
|
||||||
self._evt = bytearray(8)
|
for idx, _ in enumerate(self.report_keys):
|
||||||
|
self.report_keys[idx] = 0x00
|
||||||
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def clear_non_modifiers(self):
|
def clear_non_modifiers(self):
|
||||||
for pos in range(2, 8):
|
for idx, _ in enumerate(self.report_non_mods):
|
||||||
self._evt[pos] = 0x00
|
self.report_non_mods[idx] = 0x00
|
||||||
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def add_modifier(self, modifier):
|
def add_modifier(self, modifier):
|
||||||
if isinstance(modifier, ModifierKeycode):
|
if isinstance(modifier, ModifierKeycode):
|
||||||
self._evt[0] |= modifier.code
|
self.report_mods[0] |= modifier.code
|
||||||
else:
|
else:
|
||||||
self._evt[0] |= modifier
|
self.report_mods[0] |= modifier
|
||||||
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def remove_modifier(self, modifier):
|
def remove_modifier(self, modifier):
|
||||||
if isinstance(modifier, ModifierKeycode):
|
if isinstance(modifier, ModifierKeycode):
|
||||||
self._evt[0] ^= modifier.code
|
self.report_mods[0] ^= modifier.code
|
||||||
else:
|
else:
|
||||||
self._evt[0] ^= modifier
|
self.report_mods[0] ^= modifier
|
||||||
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def add_key(self, key):
|
def add_key(self, key):
|
||||||
# Try to find the first empty slot in the key report, and fill it
|
# Try to find the first empty slot in the key report, and fill it
|
||||||
placed = False
|
placed = False
|
||||||
for pos in range(2, 8):
|
|
||||||
if self._evt[pos] == 0x00:
|
where_to_place = self.report_non_mods
|
||||||
self._evt[pos] = key.code
|
|
||||||
|
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
|
placed = True
|
||||||
break
|
break
|
||||||
|
|
||||||
@ -155,9 +191,15 @@ class HIDHelper:
|
|||||||
|
|
||||||
def remove_key(self, key):
|
def remove_key(self, key):
|
||||||
removed = False
|
removed = False
|
||||||
for pos in range(2, 8):
|
|
||||||
if self._evt[pos] == key.code:
|
where_to_place = self.report_non_mods
|
||||||
self._evt[pos] = 0x00
|
|
||||||
|
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
|
removed = True
|
||||||
|
|
||||||
if not removed:
|
if not removed:
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
[flake8]
|
[flake8]
|
||||||
exclude = .git,__pycache__,vendor,.venv
|
exclude = .git,__pycache__,vendor,.venv
|
||||||
max_line_length = 99
|
max_line_length = 99
|
||||||
ignore = X100
|
ignore = X100, E262
|
||||||
per-file-ignores =
|
per-file-ignores =
|
||||||
user_keymaps/**/*.py: F401,E501
|
user_keymaps/**/*.py: F401,E501
|
||||||
tests/test_data/keymaps/**/*.py: F401,E501
|
tests/test_data/keymaps/**/*.py: F401,E501
|
||||||
|
@ -0,0 +1,14 @@
|
|||||||
|
try:
|
||||||
|
from collections import namedtuple
|
||||||
|
except ImportError:
|
||||||
|
from ucollections import namedtuple
|
||||||
|
|
||||||
|
HIDMode = namedtuple('HIDMode', (
|
||||||
|
'subclass',
|
||||||
|
'protocol',
|
||||||
|
'max_packet_length',
|
||||||
|
'polling_interval',
|
||||||
|
'report_descriptor',
|
||||||
|
))
|
||||||
|
|
||||||
|
hid_keyboard = HIDMode(0, 0, 0, 0, bytearray(0))
|
@ -22,8 +22,8 @@ keymap = [
|
|||||||
[KC.F, KC.G, KC.H],
|
[KC.F, KC.G, KC.H],
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
[KC.X, KC.Y, KC.Z],
|
[KC.VOLU, KC.MUTE, KC.Z],
|
||||||
[KC.TRNS, KC.PIPE, KC.O],
|
[KC.TRNS, KC.PIPE, KC.MEDIA_PLAY_PAUSE],
|
||||||
[KC.R, KC.P, KC.Q],
|
[KC.VOLD, KC.P, KC.Q],
|
||||||
],
|
],
|
||||||
]
|
]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user