Checkpoint alpha: Reflow macros and keycodes into a consistent structure. Most internal state functionality largely untouched (just moved)
This commit is contained in:
		| @@ -202,16 +202,6 @@ class Firmware: | ||||
|             if old_timeouts_len != new_timeouts_len: | ||||
|                 state_changed = True | ||||
|  | ||||
|             if self._state.macros_pending: | ||||
|                 # Blindly assume macros are going to change state, which is almost | ||||
|                 # always a safe assumption | ||||
|                 state_changed = True | ||||
|                 for macro in self._state.macros_pending: | ||||
|                     for key in macro(self): | ||||
|                         self._send_key(key) | ||||
|  | ||||
|                     self._state.resolve_macro() | ||||
|  | ||||
|             if self.debug_enabled and state_changed: | ||||
|                 print('New State: {}'.format(self._state._to_dict())) | ||||
|  | ||||
|   | ||||
							
								
								
									
										0
									
								
								kmk/handlers/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								kmk/handlers/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										108
									
								
								kmk/handlers/layers.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								kmk/handlers/layers.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,108 @@ | ||||
| from kmk.kmktime import ticks_diff, ticks_ms | ||||
|  | ||||
|  | ||||
| def df_pressed(key, state, *args, **kwargs): | ||||
|     """Switches the default layer""" | ||||
|     state.active_layers[0] = key.meta.layer | ||||
|     state.reversed_active_layers = list(reversed(state.active_layers)) | ||||
|     return state | ||||
|  | ||||
|  | ||||
| def mo_pressed(key, state, *args, **kwargs): | ||||
|     """Momentarily activates layer, switches off when you let go""" | ||||
|     state.active_layers.append(key.meta.layer) | ||||
|     state.reversed_active_layers = list(reversed(state.active_layers)) | ||||
|     return state | ||||
|  | ||||
|  | ||||
| def mo_released(key, state, KC, *args, **kwargs): | ||||
|     state.active_layers = [ | ||||
|         layer for layer in state.active_layers | ||||
|         if layer != key.meta.layer | ||||
|     ] | ||||
|     state.reversed_active_layers = list(reversed(state.active_layers)) | ||||
|     return state | ||||
|  | ||||
|  | ||||
| def lm_pressed(key, state, *args, **kwargs): | ||||
|     """As MO(layer) but with mod active""" | ||||
|     state.hid_pending = True | ||||
|     # Sets the timer start and acts like MO otherwise | ||||
|     state.start_time['lm'] = ticks_ms() | ||||
|     state.keys_pressed.add(key.meta.kc) | ||||
|     return mo_pressed(key, state, *args, **kwargs) | ||||
|  | ||||
|  | ||||
| def lm_released(key, state, *args, **kwargs): | ||||
|     """As MO(layer) but with mod active""" | ||||
|     state.hid_pending = True | ||||
|     state.keys_pressed.discard(key.meta.kc) | ||||
|     state.start_time['lm'] = None | ||||
|     return mo_released(key, state, *args, **kwargs) | ||||
|  | ||||
|  | ||||
| def lt_pressed(key, state, *args, **kwargs): | ||||
|     # Sets the timer start and acts like MO otherwise | ||||
|     state.start_time['lt'] = ticks_ms() | ||||
|     return mo_pressed(key, state, *args, **kwargs) | ||||
|  | ||||
|  | ||||
| def lt_released(key, state, *args, **kwargs): | ||||
|     # On keyup, check timer, and press key if needed. | ||||
|     if state.start_time['lt'] and ( | ||||
|         ticks_diff(ticks_ms(), state.start_time['lt']) < state.config.tap_time | ||||
|     ): | ||||
|         state.hid_pending = True | ||||
|         state.tap_key(key.meta.kc) | ||||
|  | ||||
|     mo_released(key, state, *args, **kwargs) | ||||
|     state.start_time['lt'] = None | ||||
|     return state | ||||
|  | ||||
|  | ||||
| def tg_pressed(key, state, *args, **kwargs): | ||||
|     """Toggles the layer (enables it if not active, and vise versa)""" | ||||
|     if key.meta.layer in state.active_layers: | ||||
|         state.active_layers = [ | ||||
|             layer for layer in state.active_layers | ||||
|             if layer != key.meta.layer | ||||
|         ] | ||||
|     else: | ||||
|         state.active_layers.append(key.meta.layer) | ||||
|  | ||||
|     state.reversed_active_layers = list(reversed(state.active_layers)) | ||||
|  | ||||
|     return state | ||||
|  | ||||
|  | ||||
| def to_pressed(key, state, *args, **kwargs): | ||||
|     """Activates layer and deactivates all other layers""" | ||||
|     state.active_layers = [key.meta.kc] | ||||
|     state.reversed_active_layers = list(reversed(state.active_layers)) | ||||
|  | ||||
|     return state | ||||
|  | ||||
|  | ||||
| def tt_pressed(key, state, *args, **kwargs): | ||||
|     """Momentarily activates layer if held, toggles it if tapped repeatedly""" | ||||
|     # TODO Make this work with tap dance to function more correctly, but technically works. | ||||
|     if state.start_time['tt'] is None: | ||||
|         # Sets the timer start and acts like MO otherwise | ||||
|         state.start_time['tt'] = ticks_ms() | ||||
|         return mo_pressed(key, state, *args, **kwargs) | ||||
|     elif ticks_diff(ticks_ms(), state.start_time['tt']) < state.config.tap_time: | ||||
|         state.start_time['tt'] = None | ||||
|         return tg_pressed(key, state, *args, **kwargs) | ||||
|  | ||||
|  | ||||
| def tt_released(key, state, *args, **kwargs): | ||||
|     if ( | ||||
|         state.start_time['tt'] is None or | ||||
|         ticks_diff(ticks_ms(), state.start_time['tt']) >= state.config.tap_time | ||||
|     ): | ||||
|         # On first press, works like MO. On second press, does nothing unless let up within | ||||
|         # time window, then acts like TG. | ||||
|         state.start_time['tt'] = None | ||||
|         return mo_released(key, state, *args, **kwargs) | ||||
|  | ||||
|     return state | ||||
							
								
								
									
										40
									
								
								kmk/handlers/sequences.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								kmk/handlers/sequences.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,40 @@ | ||||
| from kmk.keycodes import ALL_KEYS, KC, make_key | ||||
| from kmk.types import KeySequenceMeta | ||||
|  | ||||
|  | ||||
| def sequence_press_handler(key, state, KC, *args, **kwargs): | ||||
|     old_keys_pressed = state.keys_pressed | ||||
|     state.keys_pressed = set() | ||||
|  | ||||
|     for ikey in key.meta.seq: | ||||
|         if not getattr(ikey, 'no_press', None): | ||||
|             state.process_key(ikey, True) | ||||
|             state.config._send_hid() | ||||
|         if not getattr(ikey, 'no_release', None): | ||||
|             state.process_key(ikey, False) | ||||
|             state.config._send_hid() | ||||
|  | ||||
|     state.keys_pressed = old_keys_pressed | ||||
|  | ||||
|     return state | ||||
|  | ||||
|  | ||||
| def simple_key_sequence(seq): | ||||
|     return make_key( | ||||
|         meta=KeySequenceMeta(seq), | ||||
|         on_press=sequence_press_handler, | ||||
|     ) | ||||
|  | ||||
|  | ||||
| def send_string(message): | ||||
|     seq = [] | ||||
|  | ||||
|     for char in message: | ||||
|         kc = ALL_KEYS[char] | ||||
|  | ||||
|         if char.isupper(): | ||||
|             kc = KC.LSHIFT(kc) | ||||
|  | ||||
|         seq.append(kc) | ||||
|  | ||||
|     return simple_key_sequence(seq) | ||||
							
								
								
									
										89
									
								
								kmk/handlers/stock.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								kmk/handlers/stock.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,89 @@ | ||||
| from kmk.kmktime import sleep_ms | ||||
| from kmk.util import reset_bootloader, reset_keyboard | ||||
|  | ||||
|  | ||||
| def passthrough(key, state, *args, **kwargs): | ||||
|     return state | ||||
|  | ||||
|  | ||||
| def default_pressed(key, state, KC, coord_int=None, coord_raw=None): | ||||
|     if coord_int is not None: | ||||
|         state.coord_keys_pressed[coord_int] = key | ||||
|  | ||||
|     state.add_key(key) | ||||
|  | ||||
|     return state | ||||
|  | ||||
|  | ||||
| def default_released(key, state, KC, coord_int=None, coord_raw=None): | ||||
|     state.remove_key(key) | ||||
|  | ||||
|     if coord_int is not None: | ||||
|         state.keys_pressed.discard(key.coord_keys_pressed.get(coord_int, None)) | ||||
|         state.coord_keys_pressed[coord_int] = None | ||||
|  | ||||
|     return state | ||||
|  | ||||
|  | ||||
| def reset(*args, **kwargs): | ||||
|     reset_keyboard() | ||||
|  | ||||
|  | ||||
| def bootloader(*args, **kwargs): | ||||
|     reset_bootloader() | ||||
|  | ||||
|  | ||||
| def debug_pressed(key, state, KC, *args, **kwargs): | ||||
|     if state.config.debug_enabled: | ||||
|         print('Disabling debug mode, bye!') | ||||
|     else: | ||||
|         print('Enabling debug mode. Welcome to the jungle.') | ||||
|  | ||||
|     state.config.debug_enabled = not state.config.debug_enabled | ||||
|  | ||||
|     return state | ||||
|  | ||||
|  | ||||
| def gesc_pressed(key, state, KC, *args, **kwargs): | ||||
|     GESC_TRIGGERS = {KC.LSHIFT, KC.RSHIFT, KC.LGUI, KC.RGUI} | ||||
|  | ||||
|     if GESC_TRIGGERS.intersection(state.keys_pressed): | ||||
|         # if Shift is held, KC_GRAVE will become KC_TILDE on OS level | ||||
|         state.keys_pressed.add(KC.GRAVE) | ||||
|         return state | ||||
|  | ||||
|     # else return KC_ESC | ||||
|     state.keys_pressed.add(KC.ESCAPE) | ||||
|     state.hid_pending = True | ||||
|  | ||||
|     return state | ||||
|  | ||||
|  | ||||
| def gesc_released(key, state, KC, *args, **kwargs): | ||||
|     state.keys_pressed.discard(KC.ESCAPE) | ||||
|     state.keys_pressed.discard(KC.GRAVE) | ||||
|     state.hid_pending = True | ||||
|     return state | ||||
|  | ||||
|  | ||||
| def sleep_pressed(key, state, KC, *args, **kwargs): | ||||
|     sleep_ms(key.meta.ms) | ||||
|     return state | ||||
|  | ||||
|  | ||||
| def uc_mode_pressed(key, state, *args, **kwargs): | ||||
|     state.config.unicode_mode = key.meta.mode | ||||
|  | ||||
|     return state | ||||
|  | ||||
|  | ||||
| def leader_pressed(key, state, *args, **kwargs): | ||||
|     return state._begin_leader_mode() | ||||
|  | ||||
|  | ||||
| def tap_dance_pressed(key, state, *args, **kwargs): | ||||
|     return state._process_tap_dance(key, True) | ||||
|  | ||||
|  | ||||
| def tap_dance_released(key, state, *args, **kwargs): | ||||
|     return state._process_tap_dance(key, False) | ||||
| @@ -1,19 +1,13 @@ | ||||
| from kmk.consts import LeaderMode | ||||
| from kmk.keycodes import (FIRST_KMK_INTERNAL_KEYCODE, Keycodes, RawKeycodes, | ||||
|                           TapDanceKeycode) | ||||
| from kmk.kmktime import sleep_ms, ticks_diff, ticks_ms | ||||
| from kmk.keycodes import KC | ||||
| from kmk.kmktime import ticks_ms | ||||
| from kmk.types import TapDanceKeyMeta | ||||
| from kmk.util import intify_coordinate | ||||
|  | ||||
| GESC_TRIGGERS = { | ||||
|     Keycodes.Modifiers.KC_LSHIFT, Keycodes.Modifiers.KC_RSHIFT, | ||||
|     Keycodes.Modifiers.KC_LGUI, Keycodes.Modifiers.KC_RGUI, | ||||
| } | ||||
|  | ||||
|  | ||||
| class InternalState: | ||||
|     keys_pressed = set() | ||||
|     coord_keys_pressed = {} | ||||
|     macros_pending = [] | ||||
|     leader_pending = None | ||||
|     leader_last_len = 0 | ||||
|     hid_pending = False | ||||
| @@ -34,22 +28,6 @@ class InternalState: | ||||
|  | ||||
|     def __init__(self, config): | ||||
|         self.config = config | ||||
|         self.internal_key_handlers = { | ||||
|             RawKeycodes.KC_DF: self._layer_df, | ||||
|             RawKeycodes.KC_MO: self._layer_mo, | ||||
|             RawKeycodes.KC_LM: self._layer_lm, | ||||
|             RawKeycodes.KC_LT: self._layer_lt, | ||||
|             RawKeycodes.KC_TG: self._layer_tg, | ||||
|             RawKeycodes.KC_TO: self._layer_to, | ||||
|             RawKeycodes.KC_TT: self._layer_tt, | ||||
|             Keycodes.KMK.KC_GESC.code: self._kc_gesc, | ||||
|             RawKeycodes.KC_UC_MODE: self._kc_uc_mode, | ||||
|             RawKeycodes.KC_MACRO: self._kc_macro, | ||||
|             Keycodes.KMK.KC_LEAD.code: self._kc_lead, | ||||
|             Keycodes.KMK.KC_NO.code: self._kc_no, | ||||
|             Keycodes.KMK.KC_DEBUG.code: self._kc_debug_mode, | ||||
|             RawKeycodes.KC_TAP_DANCE: self._kc_tap_dance, | ||||
|         } | ||||
|  | ||||
|     def __repr__(self): | ||||
|         return 'InternalState({})'.format(self._to_dict()) | ||||
| @@ -74,7 +52,7 @@ class InternalState: | ||||
|         for layer in self.reversed_active_layers: | ||||
|             layer_key = self.config.keymap[layer][row][col] | ||||
|  | ||||
|             if not layer_key or layer_key == Keycodes.KMK.KC_TRNS: | ||||
|             if not layer_key or layer_key == KC.TRNS: | ||||
|                 continue | ||||
|  | ||||
|             if self.config.debug_enabled: | ||||
| @@ -122,16 +100,16 @@ class InternalState: | ||||
|             print('No key accessible for col, row: {}, {}'.format(row, col)) | ||||
|             return self | ||||
|  | ||||
|         if self.tapping and not isinstance(kc_changed, TapDanceKeycode): | ||||
|             self._process_tap_dance(kc_changed, is_pressed) | ||||
|         return self.process_key(kc_changed, is_pressed, int_coord, (row, col)) | ||||
|  | ||||
|     def process_key(self, key, is_pressed, coord_int=None, coord_raw=None): | ||||
|         if self.tapping and not isinstance(key.meta, TapDanceKeyMeta): | ||||
|             self._process_tap_dance(key, is_pressed) | ||||
|         else: | ||||
|             if is_pressed: | ||||
|                 self.coord_keys_pressed[int_coord] = kc_changed | ||||
|                 self.add_key(kc_changed) | ||||
|                 key.on_press(self, coord_int, coord_raw) | ||||
|             else: | ||||
|                 self.remove_key(kc_changed) | ||||
|                 self.keys_pressed.discard(self.coord_keys_pressed.get(int_coord, None)) | ||||
|                 self.coord_keys_pressed[int_coord] = None | ||||
|                 key.on_release(self, coord_int, coord_raw) | ||||
|  | ||||
|             if self.config.leader_mode % 2 == 1: | ||||
|                 self._process_leader_mode() | ||||
| @@ -140,27 +118,11 @@ class InternalState: | ||||
|  | ||||
|     def remove_key(self, keycode): | ||||
|         self.keys_pressed.discard(keycode) | ||||
|  | ||||
|         if keycode.code >= FIRST_KMK_INTERNAL_KEYCODE: | ||||
|             self._process_internal_key_event(keycode, False) | ||||
|         else: | ||||
|             self.hid_pending = True | ||||
|  | ||||
|         return self | ||||
|         return self.process_key(keycode, False) | ||||
|  | ||||
|     def add_key(self, keycode): | ||||
|         # TODO Make this itself a macro keycode with a keyup handler | ||||
|         #      rather than handling this inline here. Gross. | ||||
|         if keycode.code == Keycodes.KMK.KC_MACRO_SLEEP_MS: | ||||
|             sleep_ms(keycode.ms) | ||||
|         else: | ||||
|             self.keys_pressed.add(keycode) | ||||
|  | ||||
|             if keycode.code >= FIRST_KMK_INTERNAL_KEYCODE: | ||||
|                 self._process_internal_key_event(keycode, True) | ||||
|             else: | ||||
|                 self.hid_pending = True | ||||
|         return self | ||||
|         self.keys_pressed.add(keycode) | ||||
|         return self.process_key(keycode, True) | ||||
|  | ||||
|     def tap_key(self, keycode): | ||||
|         self.add_key(keycode) | ||||
| @@ -175,13 +137,6 @@ class InternalState: | ||||
|         self.hid_pending = False | ||||
|         return self | ||||
|  | ||||
|     def resolve_macro(self): | ||||
|         if self.config.debug_enabled: | ||||
|             print('Macro complete!') | ||||
|  | ||||
|         self.macros_pending.pop() | ||||
|         return self | ||||
|  | ||||
|     def _process_internal_key_event(self, changed_key, is_pressed): | ||||
|         # Since the key objects can be chained into new objects | ||||
|         # with, for example, no_press set, always check against | ||||
| @@ -192,164 +147,9 @@ class InternalState: | ||||
|             changed_key, is_pressed, | ||||
|         ) | ||||
|  | ||||
|     def _layer_df(self, changed_key, is_pressed): | ||||
|         """Switches the default layer""" | ||||
|         if is_pressed: | ||||
|             self.active_layers[0] = changed_key.layer | ||||
|             self.reversed_active_layers = list(reversed(self.active_layers)) | ||||
|  | ||||
|         return self | ||||
|  | ||||
|     def _layer_mo(self, changed_key, is_pressed): | ||||
|         """Momentarily activates layer, switches off when you let go""" | ||||
|         if is_pressed: | ||||
|             self.active_layers.append(changed_key.layer) | ||||
|         else: | ||||
|             self.active_layers = [ | ||||
|                 layer for layer in self.active_layers | ||||
|                 if layer != changed_key.layer | ||||
|             ] | ||||
|  | ||||
|         self.reversed_active_layers = list(reversed(self.active_layers)) | ||||
|  | ||||
|         return self | ||||
|  | ||||
|     def _layer_lm(self, changed_key, is_pressed): | ||||
|         """As MO(layer) but with mod active""" | ||||
|         self.hid_pending = True | ||||
|  | ||||
|         if is_pressed: | ||||
|             # Sets the timer start and acts like MO otherwise | ||||
|             self.start_time['lm'] = ticks_ms() | ||||
|             self.keys_pressed.add(changed_key.kc) | ||||
|         else: | ||||
|             self.keys_pressed.discard(changed_key.kc) | ||||
|             self.start_time['lm'] = None | ||||
|  | ||||
|         return self._layer_mo(changed_key, is_pressed) | ||||
|  | ||||
|     def _layer_lt(self, changed_key, is_pressed): | ||||
|         """Momentarily activates layer if held, sends kc if tapped""" | ||||
|         if is_pressed: | ||||
|             # Sets the timer start and acts like MO otherwise | ||||
|             self.start_time['lt'] = ticks_ms() | ||||
|             self._layer_mo(changed_key, is_pressed) | ||||
|         else: | ||||
|             # On keyup, check timer, and press key if needed. | ||||
|             if self.start_time['lt'] and ( | ||||
|                 ticks_diff(ticks_ms(), self.start_time['lt']) < self.config.tap_time | ||||
|             ): | ||||
|                 self.hid_pending = True | ||||
|                 self.tap_key(changed_key.kc) | ||||
|  | ||||
|             self._layer_mo(changed_key, is_pressed) | ||||
|             self.start_time['lt'] = None | ||||
|         return self | ||||
|  | ||||
|     def _layer_tg(self, changed_key, is_pressed): | ||||
|         """Toggles the layer (enables it if not active, and vise versa)""" | ||||
|         if is_pressed: | ||||
|             if changed_key.layer in self.active_layers: | ||||
|                 self.active_layers = [ | ||||
|                     layer for layer in self.active_layers | ||||
|                     if layer != changed_key.layer | ||||
|                 ] | ||||
|             else: | ||||
|                 self.active_layers.append(changed_key.layer) | ||||
|  | ||||
|             self.reversed_active_layers = list(reversed(self.active_layers)) | ||||
|  | ||||
|         return self | ||||
|  | ||||
|     def _layer_to(self, changed_key, is_pressed): | ||||
|         """Activates layer and deactivates all other layers""" | ||||
|         if is_pressed: | ||||
|             self.active_layers = [changed_key.layer] | ||||
|             self.reversed_active_layers = list(reversed(self.active_layers)) | ||||
|  | ||||
|         return self | ||||
|  | ||||
|     def _layer_tt(self, changed_key, is_pressed): | ||||
|         """Momentarily activates layer if held, toggles it if tapped repeatedly""" | ||||
|         # TODO Make this work with tap dance to function more correctly, but technically works. | ||||
|         if is_pressed: | ||||
|             if self.start_time['tt'] is None: | ||||
|                 # Sets the timer start and acts like MO otherwise | ||||
|                 self.start_time['tt'] = ticks_ms() | ||||
|                 return self._layer_mo(changed_key, is_pressed) | ||||
|             elif ticks_diff(ticks_ms(), self.start_time['tt']) < self.config.tap_time: | ||||
|                 self.start_time['tt'] = None | ||||
|                 return self.tg(changed_key, is_pressed) | ||||
|         elif ( | ||||
|             self.start_time['tt'] is None or | ||||
|             ticks_diff(ticks_ms(), self.start_time['tt']) >= self.config.tap_time | ||||
|         ): | ||||
|             # On first press, works like MO. On second press, does nothing unless let up within | ||||
|             # time window, then acts like TG. | ||||
|             self.start_time['tt'] = None | ||||
|             return self._layer_mo(changed_key, is_pressed) | ||||
|  | ||||
|         return self | ||||
|  | ||||
|     def _kc_uc_mode(self, changed_key, is_pressed): | ||||
|         if is_pressed: | ||||
|             self.config.unicode_mode = changed_key.mode | ||||
|  | ||||
|         return self | ||||
|  | ||||
|     def _kc_macro(self, changed_key, is_pressed): | ||||
|         if is_pressed: | ||||
|             if changed_key.keyup: | ||||
|                 self.macros_pending.append(changed_key.keyup) | ||||
|         else: | ||||
|             if changed_key.keydown: | ||||
|                 self.macros_pending.append(changed_key.keydown) | ||||
|  | ||||
|         return self | ||||
|  | ||||
|     def _kc_lead(self, changed_key, is_pressed): | ||||
|         if is_pressed: | ||||
|             self._begin_leader_mode() | ||||
|  | ||||
|         return self | ||||
|  | ||||
|     def _kc_gesc(self, changed_key, is_pressed): | ||||
|         self.hid_pending = True | ||||
|  | ||||
|         if is_pressed: | ||||
|             if GESC_TRIGGERS.intersection(self.keys_pressed): | ||||
|                 # if Shift is held, KC_GRAVE will become KC_TILDE on OS level | ||||
|                 self.keys_pressed.add(Keycodes.Common.KC_GRAVE) | ||||
|                 return self | ||||
|  | ||||
|             # else return KC_ESC | ||||
|             self.keys_pressed.add(Keycodes.Common.KC_ESCAPE) | ||||
|             return self | ||||
|  | ||||
|         self.keys_pressed.discard(Keycodes.Common.KC_ESCAPE) | ||||
|         self.keys_pressed.discard(Keycodes.Common.KC_GRAVE) | ||||
|         return self | ||||
|  | ||||
|     def _kc_no(self, changed_key, is_pressed): | ||||
|         return self | ||||
|  | ||||
|     def _kc_debug_mode(self, changed_key, is_pressed): | ||||
|         if is_pressed: | ||||
|             if self.config.debug_enabled: | ||||
|                 print('Disabling debug mode, bye!') | ||||
|             else: | ||||
|                 print('Enabling debug mode. Welcome to the jungle.') | ||||
|  | ||||
|             self.config.debug_enabled = not self.config.debug_enabled | ||||
|  | ||||
|         return self | ||||
|  | ||||
|     def _kc_tap_dance(self, changed_key, is_pressed): | ||||
|         return self._process_tap_dance(changed_key, is_pressed) | ||||
|  | ||||
|     def _process_tap_dance(self, changed_key, is_pressed): | ||||
|         if is_pressed: | ||||
|             if not isinstance(changed_key, TapDanceKeycode): | ||||
|             if not isinstance(changed_key.meta, TapDanceKeyMeta): | ||||
|                 # If we get here, changed_key is not a TapDanceKeycode and thus | ||||
|                 # the user kept typing elsewhere (presumably).  End ALL of the | ||||
|                 # currently outstanding tap dance runs. | ||||
| @@ -408,7 +208,7 @@ class InternalState: | ||||
|  | ||||
|     def _begin_leader_mode(self): | ||||
|         if self.config.leader_mode % 2 == 0: | ||||
|             self.keys_pressed.discard(Keycodes.KMK.KC_LEAD) | ||||
|             self.keys_pressed.discard(KC.LEAD) | ||||
|             # All leader modes are one number higher when activating | ||||
|             self.config.leader_mode += 1 | ||||
|  | ||||
| @@ -421,7 +221,7 @@ class InternalState: | ||||
|         lmh = tuple(self.leader_mode_history) | ||||
|  | ||||
|         if lmh in self.config.leader_dictionary: | ||||
|             self.macros_pending.append(self.config.leader_dictionary[lmh].keydown) | ||||
|             self.process_key(self.config.leader_dictionary[lmh], True) | ||||
|  | ||||
|         return self._exit_leader_mode() | ||||
|  | ||||
| @@ -438,15 +238,15 @@ class InternalState: | ||||
|         for key in keys_pressed: | ||||
|             if ( | ||||
|                 self.config.leader_mode == LeaderMode.ENTER_ACTIVE and | ||||
|                 key == Keycodes.Common.KC_ENT | ||||
|                 key == KC.ENT | ||||
|             ): | ||||
|                 self._handle_leader_sequence() | ||||
|                 break | ||||
|             elif key == Keycodes.Common.KC_ESC or key == Keycodes.KMK.KC_GESC: | ||||
|             elif key == KC.ESC or key == KC.GESC: | ||||
|                 # Clean self and turn leader mode off. | ||||
|                 self._exit_leader_mode() | ||||
|                 break | ||||
|             elif key == Keycodes.KMK.KC_LEAD: | ||||
|             elif key == KC.LEAD: | ||||
|                 break | ||||
|             else: | ||||
|                 # Add key if not needing to escape | ||||
|   | ||||
							
								
								
									
										1025
									
								
								kmk/keycodes.py
									
									
									
									
									
								
							
							
						
						
									
										1025
									
								
								kmk/keycodes.py
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,76 +0,0 @@ | ||||
| import math | ||||
|  | ||||
| from kmk.event_defs import (hid_report_event, keycode_down_event, | ||||
|                             keycode_up_event) | ||||
| from kmk.keycodes import Media | ||||
| from kmk.rotary_encoder import RotaryEncoder | ||||
|  | ||||
| VAL_FALSE = False + 1 | ||||
| VAL_NONE = True + 2 | ||||
| VAL_TRUE = True + 1 | ||||
| VOL_UP_PRESS = keycode_down_event(Media.KC_AUDIO_VOL_UP) | ||||
| VOL_UP_RELEASE = keycode_up_event(Media.KC_AUDIO_VOL_UP) | ||||
| VOL_DOWN_PRESS = keycode_down_event(Media.KC_AUDIO_VOL_DOWN) | ||||
| VOL_DOWN_RELEASE = keycode_up_event(Media.KC_AUDIO_VOL_DOWN) | ||||
|  | ||||
|  | ||||
| class RotaryEncoderMacro: | ||||
|     def __init__(self, pos_pin, neg_pin, slop_history=1, slop_threshold=1): | ||||
|         self.encoder = RotaryEncoder(pos_pin, neg_pin) | ||||
|         self.max_history = slop_history | ||||
|         self.history = bytearray(slop_history) | ||||
|         self.history_idx = 0 | ||||
|         self.history_threshold = math.floor(slop_threshold * slop_history) | ||||
|  | ||||
|     def scan(self): | ||||
|         # Anti-slop logic | ||||
|         self.history[self.history_idx] = 0 | ||||
|  | ||||
|         reading = self.encoder.direction() | ||||
|         self.history[self.history_idx] = VAL_NONE if reading is None else reading + 1 | ||||
|  | ||||
|         self.history_idx += 1 | ||||
|  | ||||
|         if self.history_idx >= self.max_history: | ||||
|             self.history_idx = 0 | ||||
|  | ||||
|         nones = 0 | ||||
|         trues = 0 | ||||
|         falses = 0 | ||||
|  | ||||
|         for val in self.history: | ||||
|             if val == VAL_NONE: | ||||
|                 nones += 1 | ||||
|             elif val == VAL_TRUE: | ||||
|                 trues += 1 | ||||
|             elif val == VAL_FALSE: | ||||
|                 falses += 1 | ||||
|  | ||||
|         if nones >= self.history_threshold: | ||||
|             return None | ||||
|  | ||||
|         if trues >= self.history_threshold: | ||||
|             return self.on_increase() | ||||
|  | ||||
|         if falses >= self.history_threshold: | ||||
|             return self.on_decrease() | ||||
|  | ||||
|     def on_decrease(self): | ||||
|         pass | ||||
|  | ||||
|     def on_increase(self): | ||||
|         pass | ||||
|  | ||||
|  | ||||
| class VolumeRotaryEncoder(RotaryEncoderMacro): | ||||
|     def on_decrease(self): | ||||
|         yield VOL_DOWN_PRESS | ||||
|         yield hid_report_event | ||||
|         yield VOL_DOWN_RELEASE | ||||
|         yield hid_report_event | ||||
|  | ||||
|     def on_increase(self): | ||||
|         yield VOL_UP_PRESS | ||||
|         yield hid_report_event | ||||
|         yield VOL_UP_RELEASE | ||||
|         yield hid_report_event | ||||
| @@ -1,28 +0,0 @@ | ||||
| from kmk.keycodes import Keycodes, Macro, char_lookup, lookup_kc_with_cache | ||||
| from kmk.string import ascii_letters, digits | ||||
|  | ||||
|  | ||||
| def simple_key_sequence(seq): | ||||
|     def _simple_key_sequence(state): | ||||
|         return seq | ||||
|  | ||||
|     return Macro(keydown=_simple_key_sequence) | ||||
|  | ||||
|  | ||||
| def send_string(message): | ||||
|     seq = [] | ||||
|  | ||||
|     for char in message: | ||||
|         kc = None | ||||
|  | ||||
|         if char in char_lookup: | ||||
|             kc = char_lookup[char] | ||||
|         elif char in ascii_letters + digits: | ||||
|             kc = lookup_kc_with_cache(char) | ||||
|  | ||||
|             if char.isupper(): | ||||
|                 kc = Keycodes.Modifiers.KC_LSHIFT(kc) | ||||
|  | ||||
|         seq.append(kc) | ||||
|  | ||||
|     return simple_key_sequence(seq) | ||||
| @@ -1,16 +1,15 @@ | ||||
| from kmk.consts import UnicodeMode | ||||
| from kmk.keycodes import (Common, Macro, Modifiers, | ||||
|                           generate_codepoint_keysym_seq) | ||||
| from kmk.keycodes import ALL_KEYS, KC, Macro | ||||
| from kmk.macros.simple import simple_key_sequence | ||||
| from kmk.types import AttrDict | ||||
| from kmk.util import get_wide_ordinal | ||||
|  | ||||
| IBUS_KEY_COMBO = Modifiers.KC_LCTRL(Modifiers.KC_LSHIFT(Common.KC_U)) | ||||
| RALT_KEY = Modifiers.KC_RALT | ||||
| U_KEY = Common.KC_U | ||||
| ENTER_KEY = Common.KC_ENTER | ||||
| RALT_DOWN_NO_RELEASE = Modifiers.KC_RALT(no_release=True) | ||||
| RALT_UP_NO_PRESS = Modifiers.KC_RALT(no_press=True) | ||||
| IBUS_KEY_COMBO = KC.LCTRL(KC.LSHIFT(KC.U)) | ||||
| RALT_KEY = KC.RALT | ||||
| U_KEY = KC.U | ||||
| ENTER_KEY = KC.ENTER | ||||
| RALT_DOWN_NO_RELEASE = KC.RALT(no_release=True) | ||||
| RALT_UP_NO_PRESS = KC.RALT(no_press=True) | ||||
|  | ||||
|  | ||||
| def compile_unicode_string_sequences(string_table): | ||||
| @@ -31,6 +30,26 @@ def unicode_string_sequence(unistring): | ||||
|     ]) | ||||
|  | ||||
|  | ||||
| def generate_codepoint_keysym_seq(codepoint, expected_length=4): | ||||
|     # To make MacOS and Windows happy, always try to send | ||||
|     # sequences that are of length 4 at a minimum | ||||
|     # On Linux systems, we can happily send longer strings. | ||||
|     # They will almost certainly break on MacOS and Windows, | ||||
|     # but this is a documentation problem more than anything. | ||||
|     # Not sure how to send emojis on Mac/Windows like that, | ||||
|     # though, since (for example) the Canadian flag is assembled | ||||
|     # from two five-character codepoints, 1f1e8 and 1f1e6 | ||||
|     # | ||||
|     # As a bonus, this function can be pretty useful for | ||||
|     # leader dictionary keys as strings. | ||||
|     seq = [KC.N0 for _ in range(max(len(codepoint), expected_length))] | ||||
|  | ||||
|     for idx, codepoint_fragment in enumerate(reversed(codepoint)): | ||||
|         seq[-(idx + 1)] = ALL_KEYS.get(codepoint_fragment) | ||||
|  | ||||
|     return seq | ||||
|  | ||||
|  | ||||
| def unicode_codepoint_sequence(codepoints): | ||||
|     kc_seqs = ( | ||||
|         generate_codepoint_keysym_seq(codepoint) | ||||
|   | ||||
| @@ -1,57 +0,0 @@ | ||||
| from kmk.pins import PULL_UP | ||||
|  | ||||
|  | ||||
| class RotaryEncoder: | ||||
|     # Please don't ask. I don't know. All I know is bit_value | ||||
|     # works as expected. Here be dragons, etc. etc. | ||||
|     MIN_VALUE = False + 1 << 1 | True + 1 | ||||
|     MAX_VALUE = True + 1 << 1 | True + 1 | ||||
|  | ||||
|     def __init__(self, pos_pin, neg_pin): | ||||
|         self.pos_pin = pos_pin | ||||
|         self.neg_pin = neg_pin | ||||
|  | ||||
|         self.pos_pin.switch_to_input(pull=PULL_UP) | ||||
|         self.neg_pin.switch_to_input(pull=PULL_UP) | ||||
|  | ||||
|         self.prev_bit_value = self.bit_value() | ||||
|  | ||||
|     def value(self): | ||||
|         return (self.pos_pin.value(), self.neg_pin.value()) | ||||
|  | ||||
|     def bit_value(self): | ||||
|         ''' | ||||
|         Returns 2, 3, 5, or 6 based on the state of the rotary encoder's two | ||||
|         bits. This is a total hack but it does what we need pretty efficiently. | ||||
|         Shrug. | ||||
|         ''' | ||||
|         return self.pos_pin.value() + 1 << 1 | self.neg_pin.value() + 1 | ||||
|  | ||||
|     def direction(self): | ||||
|         ''' | ||||
|         Compares the current rotary position against the last seen position. | ||||
|  | ||||
|         Returns True if we're rotating "positively", False if we're rotating "negatively", | ||||
|         and None if no change could safely be detected for any reason (usually this | ||||
|         means the encoder itself did not change) | ||||
|         ''' | ||||
|         new_value = self.bit_value() | ||||
|         rolling_under = self.prev_bit_value == self.MIN_VALUE and new_value == self.MAX_VALUE | ||||
|         rolling_over = self.prev_bit_value == self.MAX_VALUE and new_value == self.MIN_VALUE | ||||
|         increasing = new_value > self.prev_bit_value | ||||
|         decreasing = new_value < self.prev_bit_value | ||||
|         self.prev_bit_value = new_value | ||||
|  | ||||
|         if rolling_over: | ||||
|             return True | ||||
|         elif rolling_under: | ||||
|             return False | ||||
|  | ||||
|         if increasing: | ||||
|             return True | ||||
|         if decreasing: | ||||
|             return False | ||||
|  | ||||
|         # Either no change, or not a type of change we can safely detect, | ||||
|         # so safely do nothing | ||||
|         return None | ||||
							
								
								
									
										26
									
								
								kmk/types.py
									
									
									
									
									
								
							
							
						
						
									
										26
									
								
								kmk/types.py
									
									
									
									
									
								
							| @@ -24,3 +24,29 @@ class Anything: | ||||
| class Passthrough: | ||||
|     def __getattr__(self, attr): | ||||
|         return Anything(attr) | ||||
|  | ||||
|  | ||||
| class LayerKeyMeta: | ||||
|     def __init__(self, layer, kc=None): | ||||
|         self.layer = layer | ||||
|         self.kc = kc | ||||
|  | ||||
|  | ||||
| class KeySequenceMeta: | ||||
|     def __init__(self, seq): | ||||
|         self.seq = seq | ||||
|  | ||||
|  | ||||
| class KeySeqSleepMeta: | ||||
|     def __init__(self, ms): | ||||
|         self.ms = ms | ||||
|  | ||||
|  | ||||
| class UnicodeModeKeyMeta: | ||||
|     def __init__(self, mode): | ||||
|         self.mode = mode | ||||
|  | ||||
|  | ||||
| class TapDanceKeyMeta: | ||||
|     def __init__(self, codes): | ||||
|         self.codes = codes | ||||
|   | ||||
		Reference in New Issue
	
	Block a user