diff --git a/layout.c b/layout.c index 3639e18..c80f7d2 100644 --- a/layout.c +++ b/layout.c @@ -185,3 +185,42 @@ const int * const bb_layout_get_scancodes(lv_obj_t *keyboard, uint16_t btn_id, i const int index = sq2lv_layouts[current_layout_id].layers[layer_index].scancode_idxs[btn_id]; return &(sq2lv_layouts[current_layout_id].layers[layer_index].scancodes[index]); } + +bool bb_is_modifier(lv_obj_t *keyboard, uint16_t btn_id) { + if (current_layout_id < 0 || current_layout_id >= sq2lv_num_layouts) { + return false; + } + + int layer_index = get_layer_index(keyboard); + if (layer_index < 0 || layer_index >= sq2lv_layouts[current_layout_id].num_layers) { + return false; + } + + for (int i = 0; i < sq2lv_layouts[current_layout_id].layers[layer_index].num_modifiers; ++i) { + if (sq2lv_layouts[current_layout_id].layers[layer_index].modifier_idxs[i] == btn_id) { + return true; + } + } + + return false; +} + +const int * const bb_get_modifier_indexes(lv_obj_t *keyboard, int *num_modifiers) { + if (current_layout_id < 0 || current_layout_id >= sq2lv_num_layouts) { + *num_modifiers = 0; + return NULL; + } + + int layer_index = get_layer_index(keyboard); + if (layer_index < 0 || layer_index >= sq2lv_layouts[current_layout_id].num_layers) { + *num_modifiers = 0; + return NULL; + } + + *num_modifiers = sq2lv_layouts[current_layout_id].layers[layer_index].num_modifiers; + if (*num_modifiers == 0) { + return NULL; + } + + return &(sq2lv_layouts[current_layout_id].layers[layer_index].modifier_idxs[0]); +} diff --git a/layout.h b/layout.h index 7d53a2d..ae5b765 100644 --- a/layout.h +++ b/layout.h @@ -32,7 +32,7 @@ void bb_layout_switch_layout(lv_obj_t *keyboard, sq2lv_layout_id_t layout_id); /** - * Attempt to perform a layer switch after pressing a key. + * Attempt to perform a layer switch after pressing a key in the current layer. * * @param keyboard keyboard widget * @param btn_id button index corresponding to the pressed key @@ -41,8 +41,8 @@ void bb_layout_switch_layout(lv_obj_t *keyboard, sq2lv_layout_id_t layout_id); bool bb_layout_switch_layer(lv_obj_t *keyboard, uint16_t btn_id); /** - * Get scancodes associated with a key. - * + * Get scancodes associated with a key in the current layer. + * * @param keyboard keyboard widget * @param btn_id button index corresponding to the key * @param num_scancodes pointer to an integer into which the number of scancodes will be written @@ -50,4 +50,22 @@ bool bb_layout_switch_layer(lv_obj_t *keyboard, uint16_t btn_id); */ const int * const bb_layout_get_scancodes(lv_obj_t *keyboard, uint16_t btn_id, int *num_scancodes); +/** + * Check if a key is a modifier in the current layer. + * + * @param keyboard keyboard widget + * @param btn_id button index corresponding to the key + * @return true if the key is a modifier, false otherwise + */ +bool bb_is_modifier(lv_obj_t *keyboard, uint16_t btn_id); + +/** + * Get the button indexes for all modifier keys in the current layer. + * + * @param keyboard keyboard widget + * @param num_modifiers pointer to an integer into which the number of modifiers will be written + * @return pointer to the array of button indexes corresponding to modifier keys + */ +const int * const bb_get_modifier_indexes(lv_obj_t *keyboard, int *num_modifiers); + #endif /* BB_LAYOUT_H */ diff --git a/main.c b/main.c index 762dbf0..a85b817 100644 --- a/main.c +++ b/main.c @@ -69,6 +69,16 @@ static void set_theme(bool is_dark); */ static void keyboard_value_changed_cb(lv_event_t *event); +/** + * Emit key down and up events for a key. + * + * @param keyboard keyboard widget + * @param btn_id button index corresponding to the key + * @param key_down true if a key down event should be emitted + * @param key_up true if a key up event should be emitted + */ +static void emit_key_events(lv_obj_t *keyboard, uint16_t btn_id, bool key_down, bool key_up); + /** * Static functions @@ -90,17 +100,47 @@ static void keyboard_value_changed_cb(lv_event_t *event) { return; } + /* Note that the LV_BTNMATRIX_CTRL_CHECKED logic is inverted because LV_KEYBOARD_CTRL_BTN_FLAGS already + * contains LV_BTNMATRIX_CTRL_CHECKED. As a result, pressing e.g. CTRL will _un_check the key. To account + * for this, we invert the meaning of "checked" below. */ + + bool is_modifier = bb_is_modifier(keyboard, btn_id); + bool is_checked = !lv_btnmatrix_has_btn_ctrl(keyboard, btn_id, LV_BTNMATRIX_CTRL_CHECKED); + + /* Emit key events. Suppress key up events for modifiers unless they were unchecked. For checked modifiers + * the key up events are sent with the next non-modifier key press. */ + emit_key_events(keyboard, btn_id, true, !is_modifier || !is_checked); + + /* Pop any previously checked modifiers when a non-modifier key was pressed */ + if (!is_modifier) { + int num_modifiers = 0; + int *modifier_idxs = bb_get_modifier_indexes(keyboard, &num_modifiers); + + for (int i = 0; i < num_modifiers; ++i) { + if (!lv_btnmatrix_has_btn_ctrl(keyboard, modifier_idxs[i], LV_BTNMATRIX_CTRL_CHECKED)) { + emit_key_events(keyboard, modifier_idxs[i], false, true); + lv_btnmatrix_set_btn_ctrl(keyboard, modifier_idxs[i], LV_BTNMATRIX_CTRL_CHECKED); + } + } + } +} + +static void emit_key_events(lv_obj_t *keyboard, uint16_t btn_id, bool key_down, bool key_up) { int num_scancodes = 0; int *scancodes = bb_layout_get_scancodes(keyboard, btn_id, &num_scancodes); - /* Emit key downs in forward order */ - for (int i = 0; i < num_scancodes; ++i) { - bb_uinput_device_emit_key_down(scancodes[i]); + if (key_down) { + /* Emit key down events in forward order */ + for (int i = 0; i < num_scancodes; ++i) { + bb_uinput_device_emit_key_down(scancodes[i]); + } } - /* Emit key ups in backward order */ - for (int i = num_scancodes - 1; i >= 0; --i) { - bb_uinput_device_emit_key_up(scancodes[i]); + if (key_up) { + /* Emit key up events in backward order */ + for (int i = num_scancodes - 1; i >= 0; --i) { + bb_uinput_device_emit_key_up(scancodes[i]); + } } } diff --git a/sq2lv_layouts.c b/sq2lv_layouts.c index bb290cf..73c8e42 100644 --- a/sq2lv_layouts.c +++ b/sq2lv_layouts.c @@ -24,13 +24,19 @@ static const char * const keycaps_lower_terminal_us[] = { \ }; static const lv_btnmatrix_ctrl_t attributes_lower_terminal_us[] = { \ - LV_KEYBOARD_CTRL_BTN_FLAGS | 3, LV_KEYBOARD_CTRL_BTN_FLAGS | 3, LV_KEYBOARD_CTRL_BTN_FLAGS | 3, LV_KEYBOARD_CTRL_BTN_FLAGS | 3, LV_KEYBOARD_CTRL_BTN_FLAGS | 3, LV_KEYBOARD_CTRL_BTN_FLAGS | 3, \ + LV_KEYBOARD_CTRL_BTN_FLAGS | LV_BTNMATRIX_CTRL_CHECKABLE | 3, LV_KEYBOARD_CTRL_BTN_FLAGS | LV_BTNMATRIX_CTRL_CHECKABLE | 3, LV_KEYBOARD_CTRL_BTN_FLAGS | 3, LV_KEYBOARD_CTRL_BTN_FLAGS | 3, LV_KEYBOARD_CTRL_BTN_FLAGS | 3, LV_KEYBOARD_CTRL_BTN_FLAGS | 3, \ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, \ 2, 2, 2, 2, 2, 2, 2, 2, 2, \ LV_KEYBOARD_CTRL_BTN_FLAGS | 3, 2, 2, 2, 2, 2, 2, 2, LV_KEYBOARD_CTRL_BTN_FLAGS | 3, \ LV_KEYBOARD_CTRL_BTN_FLAGS | 3, LV_KEYBOARD_CTRL_BTN_FLAGS | 7, LV_KEYBOARD_CTRL_BTN_FLAGS | 3 \ }; +static const int num_modifiers_lower_terminal_us = 2; + +static const int modifier_idxs_lower_terminal_us[] = { \ + 0, 1 \ +}; + static const int num_switchers_lower_terminal_us = 2; static const int switcher_idxs_lower_terminal_us[] = { \ @@ -80,13 +86,19 @@ static const char * const keycaps_upper_terminal_us[] = { \ }; static const lv_btnmatrix_ctrl_t attributes_upper_terminal_us[] = { \ - LV_KEYBOARD_CTRL_BTN_FLAGS | 3, LV_KEYBOARD_CTRL_BTN_FLAGS | 3, LV_KEYBOARD_CTRL_BTN_FLAGS | 3, LV_KEYBOARD_CTRL_BTN_FLAGS | 3, LV_KEYBOARD_CTRL_BTN_FLAGS | 3, LV_KEYBOARD_CTRL_BTN_FLAGS | 3, \ + LV_KEYBOARD_CTRL_BTN_FLAGS | LV_BTNMATRIX_CTRL_CHECKABLE | 3, LV_KEYBOARD_CTRL_BTN_FLAGS | LV_BTNMATRIX_CTRL_CHECKABLE | 3, LV_KEYBOARD_CTRL_BTN_FLAGS | 3, LV_KEYBOARD_CTRL_BTN_FLAGS | 3, LV_KEYBOARD_CTRL_BTN_FLAGS | 3, LV_KEYBOARD_CTRL_BTN_FLAGS | 3, \ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, \ 2, 2, 2, 2, 2, 2, 2, 2, 2, \ LV_KEYBOARD_CTRL_BTN_FLAGS | 3, 2, 2, 2, 2, 2, 2, 2, LV_KEYBOARD_CTRL_BTN_FLAGS | 3, \ LV_KEYBOARD_CTRL_BTN_FLAGS | 3, LV_KEYBOARD_CTRL_BTN_FLAGS | 7, LV_KEYBOARD_CTRL_BTN_FLAGS | 3 \ }; +static const int num_modifiers_upper_terminal_us = 2; + +static const int modifier_idxs_upper_terminal_us[] = { \ + 0, 1 \ +}; + static const int num_switchers_upper_terminal_us = 2; static const int switcher_idxs_upper_terminal_us[] = { \ @@ -136,13 +148,19 @@ static const char * const keycaps_numbers_terminal_us[] = { \ }; static const lv_btnmatrix_ctrl_t attributes_numbers_terminal_us[] = { \ - LV_KEYBOARD_CTRL_BTN_FLAGS | 3, LV_KEYBOARD_CTRL_BTN_FLAGS | 3, LV_KEYBOARD_CTRL_BTN_FLAGS | 3, LV_KEYBOARD_CTRL_BTN_FLAGS | 3, LV_KEYBOARD_CTRL_BTN_FLAGS | 3, LV_KEYBOARD_CTRL_BTN_FLAGS | 3, \ + LV_KEYBOARD_CTRL_BTN_FLAGS | LV_BTNMATRIX_CTRL_CHECKABLE | 3, LV_KEYBOARD_CTRL_BTN_FLAGS | LV_BTNMATRIX_CTRL_CHECKABLE | 3, LV_KEYBOARD_CTRL_BTN_FLAGS | 3, LV_KEYBOARD_CTRL_BTN_FLAGS | 3, LV_KEYBOARD_CTRL_BTN_FLAGS | 3, LV_KEYBOARD_CTRL_BTN_FLAGS | 3, \ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, \ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, \ 2, 2, 2, 2, 2, 2, 2, LV_KEYBOARD_CTRL_BTN_FLAGS | 3, \ LV_KEYBOARD_CTRL_BTN_FLAGS | 3, LV_KEYBOARD_CTRL_BTN_FLAGS | 7, 2, LV_KEYBOARD_CTRL_BTN_FLAGS | 3 \ }; +static const int num_modifiers_numbers_terminal_us = 2; + +static const int modifier_idxs_numbers_terminal_us[] = { \ + 0, 1 \ +}; + static const int num_switchers_numbers_terminal_us = 1; static const int switcher_idxs_numbers_terminal_us[] = { \ @@ -188,6 +206,8 @@ static const sq2lv_layer_t layers_terminal_us[] = { .num_keys = num_keys_lower_terminal_us, .keycaps = keycaps_lower_terminal_us, .attributes = attributes_lower_terminal_us, + .num_modifiers = num_modifiers_lower_terminal_us, + .modifier_idxs = modifier_idxs_lower_terminal_us, .num_switchers = num_switchers_lower_terminal_us, .switcher_idxs = switcher_idxs_lower_terminal_us, .switcher_dests = switcher_dests_lower_terminal_us, @@ -200,6 +220,8 @@ static const sq2lv_layer_t layers_terminal_us[] = { .num_keys = num_keys_upper_terminal_us, .keycaps = keycaps_upper_terminal_us, .attributes = attributes_upper_terminal_us, + .num_modifiers = num_modifiers_upper_terminal_us, + .modifier_idxs = modifier_idxs_upper_terminal_us, .num_switchers = num_switchers_upper_terminal_us, .switcher_idxs = switcher_idxs_upper_terminal_us, .switcher_dests = switcher_dests_upper_terminal_us, @@ -212,6 +234,8 @@ static const sq2lv_layer_t layers_terminal_us[] = { .num_keys = num_keys_numbers_terminal_us, .keycaps = keycaps_numbers_terminal_us, .attributes = attributes_numbers_terminal_us, + .num_modifiers = num_modifiers_numbers_terminal_us, + .modifier_idxs = modifier_idxs_numbers_terminal_us, .num_switchers = num_switchers_numbers_terminal_us, .switcher_idxs = switcher_idxs_numbers_terminal_us, .switcher_dests = switcher_dests_numbers_terminal_us, diff --git a/sq2lv_layouts.h b/sq2lv_layouts.h index 674274b..e8cff27 100644 --- a/sq2lv_layouts.h +++ b/sq2lv_layouts.h @@ -20,6 +20,10 @@ typedef struct { const char ** const keycaps; /* Button matrix attributes */ const lv_btnmatrix_ctrl_t * const attributes; + /* Number of modifier keys */ + const int num_modifiers; + /* Button indexes of modifier keys */ + const int * const modifier_idxs; /* Number of buttons that trigger a layer switch */ const int num_switchers; /* Button indexes that trigger a layer switch */ diff --git a/squeek2lvgl b/squeek2lvgl index 5f10b71..c0e99da 160000 --- a/squeek2lvgl +++ b/squeek2lvgl @@ -1 +1 @@ -Subproject commit 5f10b71d79e33b86c157c21b25575c46ca25c254 +Subproject commit c0e99da8f58e525d87de5f3c2cf5f772d827fb1f