From 7b0bcce7ac6d3bf03f5bf302f91623521a985ded Mon Sep 17 00:00:00 2001 From: ivanjermakov Date: Fri, 28 Aug 2020 23:40:33 +0300 Subject: [PATCH] #14 layer on action --- action/action_type.py | 1 + action/layer_on_action.py | 21 ++++++++++++++++ action/mod_tap_action.py | 7 +++--- action/modified_key_action.py | 2 +- example/config.py | 19 ++++++++++---- host.py | 18 ++++++++++---- kbmap.py | 2 +- key.py | 9 ++++++- layout.py | 18 ++++++++++++++ mapper.py | 47 ++++++++++++++++++++++++++++------- 10 files changed, 119 insertions(+), 25 deletions(-) create mode 100644 action/layer_on_action.py diff --git a/action/action_type.py b/action/action_type.py index 0cb4427..c6b85c8 100644 --- a/action/action_type.py +++ b/action/action_type.py @@ -4,3 +4,4 @@ class ActionType(Enum): ModifiedKeyAction = 1 ModTapAction = 2 + LayerOnAction = 3 diff --git a/action/layer_on_action.py b/action/layer_on_action.py new file mode 100644 index 0000000..e82ca89 --- /dev/null +++ b/action/layer_on_action.py @@ -0,0 +1,21 @@ +from evdev.events import KeyEvent + +import host +import mapper +from action.action_type import ActionType +from log import debug + + +class LayerOnAction: + type: ActionType + layer: int + + def __init__(self, layer): + self.type = ActionType.LayerOnAction + self.layer = layer + + def handle(self, ui, e, config, *args): + debug('-- handling layer on action --') + mapper.active_layers[self.layer] = e.value == KeyEvent.key_down + if e.value == KeyEvent.key_up: + host.release_layer_keys(ui, self.layer, config) diff --git a/action/mod_tap_action.py b/action/mod_tap_action.py index 85783b2..c108ea9 100644 --- a/action/mod_tap_action.py +++ b/action/mod_tap_action.py @@ -3,6 +3,7 @@ from evdev.events import KeyEvent import host +import mapper from action.action_type import ActionType from log import debug @@ -17,15 +18,15 @@ def __init__(self, key, *modifiers): self.modifiers = modifiers self.key = key - def handle(self, ui, e, config, last_press_timestamp): - debug('-- handling mod tap --') + def handle(self, ui, e, config, pos, *args): + debug('-- handling mod tap action --') if e.value == KeyEvent.key_down: debug('MT is pressed, pressing modifiers') host.write_press(ui, *self.modifiers) else: debug('MT is released, releasing modifiers') host.write_release(ui, *self.modifiers) - since_last_press = (e.timestamp() - last_press_timestamp) * 1000 + since_last_press = (e.timestamp() - mapper.last_press_timestamps[pos]) * 1000 debug(f'since last press: {since_last_press}') if since_last_press <= config.tapping_term: debug('writing key press') diff --git a/action/modified_key_action.py b/action/modified_key_action.py index 0e2d704..50f40e0 100644 --- a/action/modified_key_action.py +++ b/action/modified_key_action.py @@ -16,7 +16,7 @@ def __init__(self, key, *modifiers): self.modifiers = modifiers self.key = key - def handle(self, ui, e): + def handle(self, ui, e, *args): debug('-- handling modified key action --') if e.value == KeyEvent.key_down: for m in self.modifiers: diff --git a/example/config.py b/example/config.py index c23eefb..0453d31 100644 --- a/example/config.py +++ b/example/config.py @@ -1,16 +1,25 @@ from layout import * from key import * +T______ = KC_TRANSPARENT physical_layout = laptop keymaps = [ [ KC_ESC , KC_F1 , KC_F2 , KC_F3 , KC_F4 , KC_F5 , KC_F6 , KC_F7 , KC_F8 , KC_F9 , KC_F10 , KC_F11 , KC_F12 , KC_PSCR, KC_INS , KC_DEL , - KC_GRV , KC_2 , KC_1 , KC_3 , KC_4 , KC_5 , KC_6 , KC_7 , KC_8 , KC_9 , KC_0 , KC_MINS, KC_EQL , KC_BSPC, - KC_TAB , KC_Q , KC_W , KC_E , KC_R , KC_T , KC_Y , KC_U , KC_I , KC_O , KC_P , KC_LBRC, KC_RBRC, KC_BSLS, - KC_CAPS, KC_A , KC_S , KC_D , KC_F , KC_G , KC_H , KC_J , KC_K , KC_L , KC_SCLN, KC_QUOT, KC_ENT , - KC_LSFT, KC_Z , KC_X , KC_C , KC_V , KC_B , KC_N , KC_M , KC_COMM, KC_DOT , KC_SLSH, KC_RSFT, KC_UP , - KC_LCTL, KC_LGUI, KC_LALT, LSFT_T(KC_SPC) , KC_RALT, KC_APP , KC_RCTL, KC_LEFT, KC_DOWN, KC_RGHT, + KC_GRV , KC_2 , KC_1 , KC_3 , KC_4 , KC_5 , KC_6 , KC_7 , KC_8 , KC_9 , KC_0 , KC_MINS, KC_EQL , KC_BSPC, + KC_TAB , KC_Q , KC_W , KC_E , KC_R , KC_T , KC_Y , KC_U , KC_I , KC_O , KC_P , KC_LBRC, KC_RBRC, KC_BSLS, + KC_CAPS, KC_A , KC_S , KC_D , KC_F , KC_G , KC_H , KC_J , KC_K , KC_L , KC_SCLN, KC_QUOT, KC_ENT , + KC_LSFT, KC_Z , KC_X , KC_C , KC_V , KC_B , KC_N , KC_M , KC_COMM, KC_DOT , KC_SLSH, KC_RSFT, KC_UP , + KC_LCTL, KC_LGUI, KC_LALT, LSFT_T(KC_SPC) , KC_RALT, KC_APP , KC_RCTL, KC_LEFT, KC_DOWN, KC_RGHT, + ], + [ + T______, T______, T______, T______, T______, T______, T______, T______, T______, T______, T______, T______, T______, T______, T______, T______, + T______, T______, T______, T______, T______, T______, T______, T______, T______, T______, T______, T______, T______, T______, + T______, T______, T______, T______, T______, T______, T______, T______, T______, T______, T______, T______, T______, T______, + T______, T______, T______, T______, T______, T______, T______, T______, T______, T______, T______, T______, T______, + T______, T______, T______, T______, T______, T______, T______, T______, T______, T______, T______, T______, T______, + T______, T______, T______, T______, T______, T______, T______, T______, T______, T______, ] ] diff --git a/host.py b/host.py index 72db1f2..9356717 100644 --- a/host.py +++ b/host.py @@ -1,5 +1,9 @@ from evdev import * +import key +import mapper +from log import debug + def write(ui, e): ui.write(ecodes.EV_KEY, e.code, e.value) @@ -29,8 +33,12 @@ def write_release(ui, *codes): ui.syn() -def release_keys(ui, kb): - active_keys = kb.active_keys() - for k in active_keys: - ui.write(ecodes.EV_KEY, k, KeyEvent.key_up) - ui.syn() +def release_layer_keys(ui, layer, config): + debug(f'releasing layer [{layer}] keys') + for pos, is_pressed in enumerate(mapper.layers_keys_pressed[layer]): + if is_pressed: + keycode = config.keymaps[layer][pos] + if keycode != key.KC_TRANSPARENT: + debug(f'key {keycode} released') + write_release(ui, keycode) + mapper.layers_keys_pressed[layer][pos] = False diff --git a/kbmap.py b/kbmap.py index e67918f..9763ef5 100644 --- a/kbmap.py +++ b/kbmap.py @@ -17,7 +17,7 @@ def kbmap(config_path, device_name, name, verbose): configuration. """ log.debug_enabled = verbose - mapper.map(config_path, device_name, name) + mapper.map_device(config_path, device_name, name) kbmap() diff --git a/key.py b/key.py index 4cc5ff9..a5f463e 100644 --- a/key.py +++ b/key.py @@ -1,5 +1,6 @@ from evdev.ecodes import * +from action.layer_on_action import LayerOnAction from action.mod_tap_action import ModTapAction from action.modified_key_action import ModifiedKeyAction @@ -9,7 +10,7 @@ KC_TRANSPARENT = None KC_TRNS = KC_TRANSPARENT -_______ = KC_TRANSPARENT +T______ = KC_TRANSPARENT # digits KC_0 = KEY_0 @@ -693,3 +694,9 @@ def HYPR_T(key): SWIN_T = SGUI_T SAGR_T = RSA_T ALL_T = HYPR_T + + +# layer actions + +def MO(layer): + return LayerOnAction(layer) diff --git a/layout.py b/layout.py index d5e2334..eccc53d 100644 --- a/layout.py +++ b/layout.py @@ -1,5 +1,14 @@ from key import * +tkl_transparent = [ + T______, T______, T______, T______, T______, T______, T______, T______, T______, T______, T______, T______, T______, T______, T______, T______, + T______, T______, T______, T______, T______, T______, T______, T______, T______, T______, T______, T______, T______, T______, T______, T______, T______, + T______, T______, T______, T______, T______, T______, T______, T______, T______, T______, T______, T______, T______, T______, + T______, T______, T______, T______, T______, T______, T______, T______, T______, T______, T______, T______, T______, + T______, T______, T______, T______, T______, T______, T______, T______, T______, T______, T______, T______, T______, + T______, T______, T______, T______, T______, T______, T______, T______, T______, T______, +] + tkl = [ KC_ESC , KC_F1 , KC_F2 , KC_F3 , KC_F4 , KC_F5 , KC_F6 , KC_F7 , KC_F8 , KC_F9 , KC_F10 , KC_F11 , KC_F12 , KC_PSCR, KC_SLCK, KC_PAUS, KC_GRV , KC_2 , KC_1 , KC_3 , KC_4 , KC_5 , KC_6 , KC_7 , KC_8 , KC_9 , KC_0 , KC_MINS, KC_EQL , KC_BSPC , KC_DEL , KC_END, KC_PGDN, @@ -9,6 +18,15 @@ KC_LCTL, KC_LGUI, KC_LALT, KC_SPC , KC_RALT, KC_APP , KC_RCTL, KC_LEFT, KC_DOWN, KC_RGHT, ] +laptop_transparent = [ + T______, T______, T______, T______, T______, T______, T______, T______, T______, T______, T______, T______, T______, T______, T______, T______, + T______, T______, T______, T______, T______, T______, T______, T______, T______, T______, T______, T______, T______, T______, + T______, T______, T______, T______, T______, T______, T______, T______, T______, T______, T______, T______, T______, T______, + T______, T______, T______, T______, T______, T______, T______, T______, T______, T______, T______, T______, T______, + T______, T______, T______, T______, T______, T______, T______, T______, T______, T______, T______, T______, T______, + T______, T______, T______, T______, T______, T______, T______, T______, T______, T______, +] + laptop = [ KC_ESC , KC_F1 , KC_F2 , KC_F3 , KC_F4 , KC_F5 , KC_F6 , KC_F7 , KC_F8 , KC_F9 , KC_F10 , KC_F11 , KC_F12 , KC_PSCR, KC_INS , KC_DEL , KC_GRV , KC_2 , KC_1 , KC_3 , KC_4 , KC_5 , KC_6 , KC_7 , KC_8 , KC_9 , KC_0 , KC_MINS, KC_EQL , KC_BSPC, diff --git a/mapper.py b/mapper.py index 372761e..0f0f04b 100644 --- a/mapper.py +++ b/mapper.py @@ -6,10 +6,13 @@ from evdev.events import KeyEvent import host +import key import keyboard from log import debug last_press_timestamps = [] +active_layers = [] +layers_keys_pressed = [] def load_config(path): @@ -21,7 +24,7 @@ def load_config(path): return config -def map(config_path, kb_name, ui_name='kbmap'): +def map_device(config_path, kb_name, ui_name='kbmap'): """ Create virtual device with uinput_name that will remap keyboard events from device with name device_name using config_pth configuration. @@ -33,7 +36,15 @@ def map(config_path, kb_name, ui_name='kbmap'): config = load_config(config_path) global last_press_timestamps - last_press_timestamps = [None for i in range(len(config.physical_layout))] + last_press_timestamps = [None for _ in range(len(config.physical_layout))] + global active_layers + active_layers = [False for _ in range(len(config.keymaps))] + # base layer is always active + active_layers[0] = True + global layers_keys_pressed + layers_keys_pressed = [[] for _ in range(len(active_layers))] + for i in range(len(active_layers)): + layers_keys_pressed[i] = [False for _ in range(len(config.keymaps[i]))] kb = keyboard.get_device_by_name(kb_name) keyboard.grab(kb) @@ -49,7 +60,7 @@ def map(config_path, kb_name, ui_name='kbmap'): def handle_event(e, kb, ui, config): - global last_press_timestamps + global layers_keys_pressed debug(f'-------- handling {e} --------') pos = map_key_to_pos(e.code, config) @@ -58,14 +69,15 @@ def handle_event(e, kb, ui, config): debug(f'key is {ecodes.KEY[e.code]} ({e.code}) at {pos}') - keycode = config.keymaps[0][pos] + key, layer_index = find_key(pos, config) + layers_keys_pressed[layer_index][pos] = e.value == KeyEvent.key_down - if hasattr(keycode, 'type'): - debug(f'key is mapped to action of type {keycode.type} at {pos}') - keycode.handle(ui, e, config, last_press_timestamps[pos]) + if hasattr(key, 'type'): + debug(f'key is mapped to action of type {key.type} at {pos}') + key.handle(ui, e, config, pos) else: - debug(f'key is mapped to key: {ecodes.KEY[keycode]} ({keycode}) at {pos}') - host.write_code(ui, keycode, e.value) + debug(f'key is mapped to key: {ecodes.KEY[key]} ({key}) at {pos}') + host.write_code(ui, key, e.value) update_timestamps(pos, e) @@ -84,3 +96,20 @@ def update_timestamps(pos, e): new_value = e.timestamp() if e.value == KeyEvent.key_down else None last_press_timestamps[pos] = new_value debug(f'updated timestamp at [{pos}] to {new_value}') + + +def find_key(pos, config): + """ + Find which key to use regarding active layers and key position. + Picked up first non-KC_TRANS key within active layers. + Layers with higher index have higher precedence. + + :param pos + :param config + :return key or action + """ + global active_layers + for layer_index, layer in reversed(list(enumerate(config.keymaps))): + layer_key = layer[pos] + if active_layers[layer_index] and layer_key != key.KC_TRANSPARENT: + return layer_key, layer_index