Support KEYBOARD and CONSUMER modes of HID on Feather M4 Express
This commit is contained in:
		
							
								
								
									
										39
									
								
								kmk/circuitpython/hid.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								kmk/circuitpython/hid.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,39 @@ | |||||||
|  | import usb_hid | ||||||
|  | from kmk.common.abstract.hid import AbstractHidHelper | ||||||
|  | from kmk.common.consts import (HID_REPORT_SIZES, HIDReportTypes, HIDUsage, | ||||||
|  |                                HIDUsagePage) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class HIDHelper(AbstractHidHelper): | ||||||
|  |     REPORT_BYTES = 9 | ||||||
|  |  | ||||||
|  |     def post_init(self): | ||||||
|  |         self.devices = {} | ||||||
|  |  | ||||||
|  |         for device in usb_hid.devices: | ||||||
|  |             if device.usage_page == HIDUsagePage.CONSUMER and device.usage == HIDUsage.CONSUMER: | ||||||
|  |                 self.devices[HIDReportTypes.CONSUMER] = device | ||||||
|  |                 continue | ||||||
|  |  | ||||||
|  |             if device.usage_page == HIDUsagePage.KEYBOARD and device.usage == HIDUsage.KEYBOARD: | ||||||
|  |                 self.devices[HIDReportTypes.KEYBOARD] = device | ||||||
|  |                 continue | ||||||
|  |  | ||||||
|  |             if device.usage_page == HIDUsagePage.MOUSE and device.usage == HIDUsage.MOUSE: | ||||||
|  |                 self.devices[HIDReportTypes.MOUSE] = device | ||||||
|  |                 continue | ||||||
|  |  | ||||||
|  |             if ( | ||||||
|  |                 device.usage_page == HIDUsagePage.SYSCONTROL and | ||||||
|  |                 device.usage == HIDUsage.SYSCONTROL | ||||||
|  |             ): | ||||||
|  |                 self.devices[HIDReportTypes.SYSCONTROL] = device | ||||||
|  |                 continue | ||||||
|  |  | ||||||
|  |     def hid_send(self, evt): | ||||||
|  |         # int, can be looked up in HIDReportTypes | ||||||
|  |         reporting_device_const = self.report_device[0] | ||||||
|  |  | ||||||
|  |         return self.devices[reporting_device_const].send_report( | ||||||
|  |             evt[1:HID_REPORT_SIZES[reporting_device_const] + 1], | ||||||
|  |         ) | ||||||
							
								
								
									
										155
									
								
								kmk/common/abstract/hid.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										155
									
								
								kmk/common/abstract/hid.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,155 @@ | |||||||
|  | import logging | ||||||
|  |  | ||||||
|  | from kmk.common.consts import HIDReportTypes | ||||||
|  | from kmk.common.event_defs import HID_REPORT_EVENT | ||||||
|  | from kmk.common.keycodes import (FIRST_KMK_INTERNAL_KEYCODE, ConsumerKeycode, | ||||||
|  |                                  ModifierKeycode) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class AbstractHidHelper: | ||||||
|  |     REPORT_BYTES = 8 | ||||||
|  |  | ||||||
|  |     def __init__(self, store, log_level=logging.NOTSET): | ||||||
|  |         self.logger = logging.getLogger(__name__) | ||||||
|  |         self.logger.setLevel(log_level) | ||||||
|  |  | ||||||
|  |         self.store = store | ||||||
|  |         self.store.subscribe( | ||||||
|  |             lambda state, action: self._subscription(state, action), | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  |         self._evt = bytearray(self.REPORT_BYTES) | ||||||
|  |         self.report_device = memoryview(self._evt)[0:1] | ||||||
|  |         self.report_device[0] = HIDReportTypes.KEYBOARD | ||||||
|  |  | ||||||
|  |         # 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:] | ||||||
|  |  | ||||||
|  |         self.post_init() | ||||||
|  |  | ||||||
|  |     def post_init(self): | ||||||
|  |         pass | ||||||
|  |  | ||||||
|  |     def _subscription(self, state, action): | ||||||
|  |         if action.type == HID_REPORT_EVENT: | ||||||
|  |             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() | ||||||
|  |  | ||||||
|  |             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() | ||||||
|  |  | ||||||
|  |     def hid_send(self, evt): | ||||||
|  |         raise NotImplementedError('hid_send(evt) must be implemented') | ||||||
|  |  | ||||||
|  |     def send(self): | ||||||
|  |         self.logger.debug('Sending HID report: {}'.format(self._evt)) | ||||||
|  |         self.hid_send(self._evt) | ||||||
|  |  | ||||||
|  |         return self | ||||||
|  |  | ||||||
|  |     def clear_all(self): | ||||||
|  |         for idx, _ in enumerate(self.report_keys): | ||||||
|  |             self.report_keys[idx] = 0x00 | ||||||
|  |  | ||||||
|  |         return self | ||||||
|  |  | ||||||
|  |     def clear_non_modifiers(self): | ||||||
|  |         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.report_mods[0] |= modifier.code | ||||||
|  |         else: | ||||||
|  |             self.report_mods[0] |= modifier | ||||||
|  |  | ||||||
|  |         return self | ||||||
|  |  | ||||||
|  |     def remove_modifier(self, modifier): | ||||||
|  |         if isinstance(modifier, ModifierKeycode): | ||||||
|  |             self.report_mods[0] ^= modifier.code | ||||||
|  |         else: | ||||||
|  |             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 | ||||||
|  |  | ||||||
|  |         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 | ||||||
|  |  | ||||||
|  |         if not placed: | ||||||
|  |             self.logger.warning('Out of space in HID report, could not add key') | ||||||
|  |  | ||||||
|  |         return self | ||||||
|  |  | ||||||
|  |     def remove_key(self, key): | ||||||
|  |         removed = False | ||||||
|  |  | ||||||
|  |         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: | ||||||
|  |             self.logger.warning('Tried to remove key that was not added') | ||||||
|  |  | ||||||
|  |         return self | ||||||
| @@ -5,10 +5,33 @@ class HIDReportTypes: | |||||||
|     SYSCONTROL = 4 |     SYSCONTROL = 4 | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class HIDUsage: | ||||||
|  |     KEYBOARD = 0x06 | ||||||
|  |     MOUSE = 0x02 | ||||||
|  |     CONSUMER = 0x01 | ||||||
|  |     SYSCONTROL = 0x80 | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class HIDUsagePage: | ||||||
|  |     CONSUMER = 0x0C | ||||||
|  |     KEYBOARD = MOUSE = SYSCONTROL = 0x01 | ||||||
|  |  | ||||||
|  |  | ||||||
|  | # Currently only used by the CircuitPython HIDHelper because CircuitPython | ||||||
|  | # actually enforces these limits with a ValueError. Unused on PyBoard because | ||||||
|  | # we can happily send full reports there and it magically works. | ||||||
|  | HID_REPORT_SIZES = { | ||||||
|  |     HIDReportTypes.KEYBOARD: 8, | ||||||
|  |     HIDReportTypes.MOUSE: 4, | ||||||
|  |     HIDReportTypes.CONSUMER: 2, | ||||||
|  |     HIDReportTypes.SYSCONTROL: 8,  # TODO find the correct value for this | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
| HID_REPORT_STRUCTURE = bytes([ | HID_REPORT_STRUCTURE = bytes([ | ||||||
|     # Regular keyboard |     # Regular keyboard | ||||||
|     0x05, 0x01,  # Usage Page (Generic Desktop) |     0x05, HIDUsagePage.KEYBOARD,  # Usage Page (Generic Desktop) | ||||||
|     0x09, 0x06,  # Usage (Keyboard) |     0x09, HIDUsage.KEYBOARD,  # Usage (Keyboard) | ||||||
|     0xA1, 0x01,  # Collection (Application) |     0xA1, 0x01,  # Collection (Application) | ||||||
|     0x85, HIDReportTypes.KEYBOARD,  #   Report ID (1) |     0x85, HIDReportTypes.KEYBOARD,  #   Report ID (1) | ||||||
|     0x05, 0x07,  #   Usage Page (Keyboard) |     0x05, 0x07,  #   Usage Page (Keyboard) | ||||||
| @@ -39,8 +62,8 @@ HID_REPORT_STRUCTURE = bytes([ | |||||||
|     0x91, 0x01,  #   Output (Constant) |     0x91, 0x01,  #   Output (Constant) | ||||||
|     0xC0,        # End Collection |     0xC0,        # End Collection | ||||||
|     # Regular mouse |     # Regular mouse | ||||||
|     0x05, 0x01,  # Usage Page (Generic Desktop) |     0x05, HIDUsagePage.MOUSE,  # Usage Page (Generic Desktop) | ||||||
|     0x09, 0x02,  # Usage (Mouse) |     0x09, HIDUsage.MOUSE,  # Usage (Mouse) | ||||||
|     0xA1, 0x01,  # Collection (Application) |     0xA1, 0x01,  # Collection (Application) | ||||||
|     0x09, 0x01,  #   Usage (Pointer) |     0x09, 0x01,  #   Usage (Pointer) | ||||||
|     0xA1, 0x00,  #   Collection (Physical) |     0xA1, 0x00,  #   Collection (Physical) | ||||||
| @@ -73,8 +96,8 @@ HID_REPORT_STRUCTURE = bytes([ | |||||||
|     0xC0,        #   End Collection |     0xC0,        #   End Collection | ||||||
|     0xC0,        # End Collection |     0xC0,        # End Collection | ||||||
|     # Consumer ("multimedia") keys |     # Consumer ("multimedia") keys | ||||||
|     0x05, 0x0C,        # Usage Page (Consumer) |     0x05, HIDUsagePage.CONSUMER,  # Usage Page (Consumer) | ||||||
|     0x09, 0x01,        # Usage (Consumer Control) |     0x09, HIDUsage.CONSUMER,  # Usage (Consumer Control) | ||||||
|     0xA1, 0x01,        # Collection (Application) |     0xA1, 0x01,        # Collection (Application) | ||||||
|     0x85, HIDReportTypes.CONSUMER,  # Report ID (n) |     0x85, HIDReportTypes.CONSUMER,  # Report ID (n) | ||||||
|     0x75, 0x10,        #   Report Size (16) |     0x75, 0x10,        #   Report Size (16) | ||||||
| @@ -86,8 +109,8 @@ HID_REPORT_STRUCTURE = bytes([ | |||||||
|     0x81, 0x00,        #   Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) |     0x81, 0x00,        #   Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) | ||||||
|     0xC0,              # End Collection |     0xC0,              # End Collection | ||||||
|     # Power controls |     # Power controls | ||||||
|     0x05, 0x01,        # Usage Page (Generic Desktop Ctrls) |     0x05, HIDUsagePage.SYSCONTROL,  # Usage Page (Generic Desktop Ctrls) | ||||||
|     0x09, 0x80,        # Usage (Sys Control) |     0x09, HIDUsage.SYSCONTROL,  # Usage (Sys Control) | ||||||
|     0xA1, 0x01,        # Collection (Application) |     0xA1, 0x01,        # Collection (Application) | ||||||
|     0x85, HIDReportTypes.SYSCONTROL,  # Report ID (n) |     0x85, HIDReportTypes.SYSCONTROL,  # Report ID (n) | ||||||
|     0x75, 0x02,        #   Report Size (2) |     0x75, 0x02,        #   Report Size (2) | ||||||
|   | |||||||
| @@ -52,7 +52,7 @@ class Store: | |||||||
|                     cb(self.state, action) |                     cb(self.state, action) | ||||||
|                 except Exception as e: |                 except Exception as e: | ||||||
|                     self.logger.error('Callback failed, moving on') |                     self.logger.error('Callback failed, moving on') | ||||||
|                     print(sys.print_exception(e), file=sys.stderr) |                     sys.print_exception(e) | ||||||
|  |  | ||||||
|     def get_state(self): |     def get_state(self): | ||||||
|         return self.state |         return self.state | ||||||
|   | |||||||
| @@ -1,6 +1,7 @@ | |||||||
| import sys | import sys | ||||||
| from logging import DEBUG | from logging import DEBUG | ||||||
|  |  | ||||||
|  | from kmk.circuitpython.hid import HIDHelper | ||||||
| from kmk.circuitpython.matrix import MatrixScanner | from kmk.circuitpython.matrix import MatrixScanner | ||||||
| from kmk.common.consts import UnicodeModes | from kmk.common.consts import UnicodeModes | ||||||
| from kmk.firmware import Firmware | from kmk.firmware import Firmware | ||||||
| @@ -23,6 +24,7 @@ def main(): | |||||||
|             unicode_mode=unicode_mode, |             unicode_mode=unicode_mode, | ||||||
|             log_level=DEBUG, |             log_level=DEBUG, | ||||||
|             matrix_scanner=MatrixScanner, |             matrix_scanner=MatrixScanner, | ||||||
|  |             hid=HIDHelper, | ||||||
|         ) |         ) | ||||||
|  |  | ||||||
|         firmware.go() |         firmware.go() | ||||||
|   | |||||||
| @@ -1,11 +1,7 @@ | |||||||
| import logging |  | ||||||
|  |  | ||||||
| from pyb import USB_HID, delay, hid_keyboard | from pyb import USB_HID, delay, hid_keyboard | ||||||
|  |  | ||||||
| from kmk.common.consts import HID_REPORT_STRUCTURE, HIDReportTypes | from kmk.common.abstract.hid import AbstractHidHelper | ||||||
| from kmk.common.event_defs import HID_REPORT_EVENT | from kmk.common.consts import HID_REPORT_STRUCTURE | ||||||
| from kmk.common.keycodes import (FIRST_KMK_INTERNAL_KEYCODE, ConsumerKeycode, |  | ||||||
|                                  ModifierKeycode) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def generate_pyb_hid_descriptor(): | def generate_pyb_hid_descriptor(): | ||||||
| @@ -14,80 +10,20 @@ def generate_pyb_hid_descriptor(): | |||||||
|     return tuple(existing_keyboard) |     return tuple(existing_keyboard) | ||||||
|  |  | ||||||
|  |  | ||||||
| class HIDHelper: | class HIDHelper(AbstractHidHelper): | ||||||
|     def __init__(self, store, log_level=logging.NOTSET): |     # For some bizarre reason this can no longer be 8, it'll just fail to send | ||||||
|         self.logger = logging.getLogger(__name__) |     # anything. This is almost certainly a bug in the report descriptor sent | ||||||
|         self.logger.setLevel(log_level) |     # 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.store = store |     REPORT_BYTES = 7 | ||||||
|         self.store.subscribe( |  | ||||||
|             lambda state, action: self._subscription(state, action), |  | ||||||
|         ) |  | ||||||
|  |  | ||||||
|  |     def post_init(self): | ||||||
|         self._hid = USB_HID() |         self._hid = USB_HID() | ||||||
|  |         self.hid_send = self._hid.send | ||||||
|         # 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 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() |  | ||||||
|  |  | ||||||
|             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() |  | ||||||
|  |  | ||||||
|     def send(self): |     def send(self): | ||||||
|         self.logger.debug('Sending HID report: {}'.format(self._evt)) |         self.logger.debug('Sending HID report: {}'.format(self._evt)) | ||||||
|         self._hid.send(self._evt) |         self.hid_send(self._evt) | ||||||
|  |  | ||||||
|         # Without this delay, events get clobbered and you'll likely end up with |         # Without this delay, events get clobbered and you'll likely end up with | ||||||
|         # a string like `heloooooooooooooooo` rather than `hello`. This number |         # a string like `heloooooooooooooooo` rather than `hello`. This number | ||||||
| @@ -101,69 +37,3 @@ class HIDHelper: | |||||||
|         delay(5) |         delay(5) | ||||||
|  |  | ||||||
|         return self |         return self | ||||||
|  |  | ||||||
|     def clear_all(self): |  | ||||||
|         for idx, _ in enumerate(self.report_keys): |  | ||||||
|             self.report_keys[idx] = 0x00 |  | ||||||
|  |  | ||||||
|         return self |  | ||||||
|  |  | ||||||
|     def clear_non_modifiers(self): |  | ||||||
|         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.report_mods[0] |= modifier.code |  | ||||||
|         else: |  | ||||||
|             self.report_mods[0] |= modifier |  | ||||||
|  |  | ||||||
|         return self |  | ||||||
|  |  | ||||||
|     def remove_modifier(self, modifier): |  | ||||||
|         if isinstance(modifier, ModifierKeycode): |  | ||||||
|             self.report_mods[0] ^= modifier.code |  | ||||||
|         else: |  | ||||||
|             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 |  | ||||||
|  |  | ||||||
|         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 |  | ||||||
|  |  | ||||||
|         if not placed: |  | ||||||
|             self.logger.warning('Out of space in HID report, could not add key') |  | ||||||
|  |  | ||||||
|         return self |  | ||||||
|  |  | ||||||
|     def remove_key(self, key): |  | ||||||
|         removed = False |  | ||||||
|  |  | ||||||
|         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: |  | ||||||
|             self.logger.warning('Tried to remove key that was not added') |  | ||||||
|  |  | ||||||
|         return self |  | ||||||
|   | |||||||
| @@ -49,7 +49,7 @@ ANGRY_TABLE_FLIP = unicode_sequence([ | |||||||
| keymap = [ | keymap = [ | ||||||
|     [ |     [ | ||||||
|         [KC.GESC,              KC.A,     KC.RESET], |         [KC.GESC,              KC.A,     KC.RESET], | ||||||
|         [KC.MO(1),             KC.B,     KC.C], |         [KC.MO(1),             KC.B,     KC.MUTE], | ||||||
|         [KC.LT(2, KC.EXCLAIM), KC.HASH,  KC.ENTER], |         [KC.LT(2, KC.EXCLAIM), KC.HASH,  KC.ENTER], | ||||||
|         [KC.TT(3),             KC.SPACE, KC.LSHIFT], |         [KC.TT(3),             KC.SPACE, KC.LSHIFT], | ||||||
|     ], |     ], | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user