// --------------------------------------------------------------------------- // Работа с шифтом // --------------------------------------------------------------------------- Shift shift_should_be = 0; Shift shift_current = 0; uint32_t shift_timer = 0; uint8_t shift_pressed_count = 0; Key shift_get_key(Key key) { switch (key) { case KS_GRV: return KC_GRV; case KS_TILD: return KC_GRV; case KS_1: return KC_1; case KS_EXCL: return KC_1; case KS_2: return KC_2; case KS_AT: return KC_2; case KS_3: return KC_3; case KS_HASH: return KC_3; case KS_4: return KC_4; case KS_DLR: return KC_4; case KS_5: return KC_5; case KS_PERC: return KC_5; case KS_6: return KC_6; case KS_CIRC: return KC_6; case KS_7: return KC_7; case KS_AMPR: return KC_7; case KS_8: return KC_8; case KS_ASTR: return KC_8; case KS_9: return KC_9; case KS_LPRN: return KC_9; case KS_0: return KC_0; case KS_RPRN: return KC_0; case KS_MINS: return KC_MINS; case KS_UNDS: return KC_MINS; case KS_EQL: return KC_EQL; case KS_PLUS: return KC_EQL; case KS_Q: return KC_Q; case KS_S_Q: return KC_Q; case KS_W: return KC_W; case KS_S_W: return KC_W; case KS_E: return KC_E; case KS_S_E: return KC_E; case KS_R: return KC_R; case KS_S_R: return KC_R; case KS_T: return KC_T; case KS_S_T: return KC_T; case KS_Y: return KC_Y; case KS_S_Y: return KC_Y; case KS_U: return KC_U; case KS_S_U: return KC_U; case KS_I: return KC_I; case KS_S_I: return KC_I; case KS_O: return KC_O; case KS_S_O: return KC_O; case KS_P: return KC_P; case KS_S_P: return KC_P; case KS_LBRC: return KC_LBRC; case KS_LCBR: return KC_LBRC; case KS_RBRC: return KC_RBRC; case KS_RCBR: return KC_RBRC; case KS_A: return KC_A; case KS_S_A: return KC_A; case KS_S: return KC_S; case KS_S_S: return KC_S; case KS_D: return KC_D; case KS_S_D: return KC_D; case KS_F: return KC_F; case KS_S_F: return KC_F; case KS_G: return KC_G; case KS_S_G: return KC_G; case KS_H: return KC_H; case KS_S_H: return KC_H; case KS_J: return KC_J; case KS_S_J: return KC_J; case KS_K: return KC_K; case KS_S_K: return KC_K; case KS_L: return KC_L; case KS_S_L: return KC_L; case KS_SCLN: return KC_SCLN; case KS_COLN: return KC_SCLN; case KS_QUOT: return KC_QUOT; case KS_DQUO: return KC_QUOT; case KS_BSLS: return KC_BSLS; case KS_PIPE: return KC_BSLS; case KS_Z: return KC_Z; case KS_S_Z: return KC_Z; case KS_X: return KC_X; case KS_S_X: return KC_X; case KS_C: return KC_C; case KS_S_C: return KC_C; case KS_V: return KC_V; case KS_S_V: return KC_V; case KS_B: return KC_B; case KS_S_B: return KC_B; case KS_N: return KC_N; case KS_S_N: return KC_N; case KS_M: return KC_M; case KS_S_M: return KC_M; case KS_COMM: return KC_COMM; case KS_LT: return KC_COMM; case KS_DOT: return KC_DOT; case KS_GT: return KC_DOT; case KS_SLSH: return KC_SLSH; case KS_QUES: return KC_SLSH; default: return NONE_KEY; } } Shift shift_get_shift(Key key) { if (KS_GRV <= key && key <= KS_QUES) { return (key - KS_GRV) % 2; } else { return NONE_SHIFT; } } void shift_activate(Shift shift) { if (shift_current != shift) { shift_timer = timer_read(); if (shift) { register_code(KC_LSFT); } else { unregister_code(KC_LSFT); } } shift_current = shift; } void shift_activate_from_user(Shift shift) { shift_should_be = shift; shift_activate(shift); } Key shift_process(Key key, bool down) { Shift new_shift = shift_get_shift(key); if (down) { if (new_shift != NONE_SHIFT) { shift_activate(new_shift); } else { shift_activate(shift_should_be); } } if (new_shift != NONE_SHIFT) { if (down) { shift_pressed_count++; } else { shift_pressed_count--; } } return shift_get_key(key); } void shift_user_timer(void) { // Нужно выключать шифт после прохождения определённого времени, потому что пользователь ожидает как будто шифт на самом деле включён if (shift_pressed_count == 0 && shift_current != shift_should_be && timer_read() - shift_timer >= 100) { shift_activate(shift_should_be); shift_timer = timer_read(); } } // --------------------------------------------------------------------------- // Работа с одиночным шифтом // --------------------------------------------------------------------------- uint8_t shift_once_disable_stage = 2; uint8_t shift_once_layer_off = 0; uint8_t shift_once_layer_current = 0; uint32_t shift_once_enabled_time = 0; bool shift_once_can_disable = true; bool shift_once_is_enabled(void) { return shift_once_disable_stage == 2; } void shift_once_use_to_next_key(uint8_t layer) { if (shift_current == 0) { shift_activate_from_user(true); layer_on(layer); shift_once_disable_stage = 2; shift_once_layer_off = layer; shift_once_enabled_time = timer_read(); } } void shift_once_process_key(uint8_t layer, bool down) { if (down) { shift_once_use_to_next_key(layer); shift_once_can_disable = false; } else { shift_once_can_disable = true; shift_once_enabled_time = timer_read(); } } void shift_once_disable(void) { if (shift_once_disable_stage == 2) { layer_off(shift_once_layer_off); shift_activate_from_user(false); shift_once_disable_stage = 0; } } void shift_once_process(Key key, keyrecord_t* record) { bool down = record->event.pressed; if (shift_once_disable_stage == 1) { shift_once_disable_stage = 0; shift_activate_from_user(false); } if (down && key != SFT_N_O && shift_once_disable_stage == 2) { shift_once_disable_stage = 1; layer_off(shift_once_layer_off); } } void shift_once_user_timer(void) { if (shift_once_can_disable && shift_once_is_enabled() && timer_read() - shift_once_enabled_time >= 1000) { shift_once_disable(); } } // --------------------------------------------------------------------------- // Работа с языком // --------------------------------------------------------------------------- Lang lang_should_be = 0; Lang lang_current = 0; uint32_t lang_timer = 0; uint8_t lang_pressed_count = 0; Key lang_get_key(Key key) { if (EN_GRV <= key && key <= EN_QUES) { return (key - EN_GRV) + KS_GRV; } else if (RU_JO <= key && key <= RU_COMM) { return (key - RU_JO) + KS_GRV; } else { return NONE_KEY; } } Lang lang_get_lang(Key key) { if (EN_GRV <= key && key <= EN_QUES) { return 0; } else if (RU_JO <= key && key <= RU_COMM) { return 1; } else { return NONE_LANG; } } Key lang_calc_agnostic(Key key) { if (lang_current == 0) { switch (key) { case AG_1: return EN_1; case AG_2: return EN_2; case AG_3: return EN_3; case AG_4: return EN_4; case AG_5: return EN_5; case AG_6: return EN_6; case AG_7: return EN_7; case AG_8: return EN_8; case AG_9: return EN_9; case AG_0: return EN_0; case AG_EXCL: return EN_EXCL; case AG_PERC: return EN_PERC; case AG_ASTR: return EN_ASTR; case AG_LPRN: return EN_LPRN; case AG_RPRN: return EN_RPRN; case AG_MINS: return EN_MINS; case AG_UNDS: return EN_UNDS; case AG_EQL: return EN_EQL; case AG_PLUS: return EN_PLUS; case AG_SCLN: return EN_SCLN; case AG_COLN: return EN_COLN; case AG_DQUO: return EN_DQUO; case AG_BSLS: return EN_BSLS; case AG_COMM: return EN_COMM; case AG_DOT: return EN_DOT; case AG_SLSH: return EN_SLSH; case AG_QUES: return EN_QUES; default: return NONE_KEY; } } else { switch (key) { case AG_1: return RU_1; case AG_2: return RU_2; case AG_3: return RU_3; case AG_4: return RU_4; case AG_5: return RU_5; case AG_6: return RU_6; case AG_7: return RU_7; case AG_8: return RU_8; case AG_9: return RU_9; case AG_0: return RU_0; case AG_EXCL: return RU_EXCL; case AG_PERC: return RU_PERC; case AG_ASTR: return RU_ASTR; case AG_LPRN: return RU_LPRN; case AG_RPRN: return RU_RPRN; case AG_MINS: return RU_MINS; case AG_UNDS: return RU_UNDS; case AG_EQL: return RU_EQL; case AG_PLUS: return RU_PLUS; case AG_SCLN: return RU_SCLN; case AG_COLN: return RU_COLN; case AG_DQUO: return RU_DQUO; case AG_BSLS: return RU_BSLS; case AG_COMM: return RU_COMM; case AG_DOT: return RU_DOT; case AG_SLSH: return RU_SLSH; case AG_QUES: return RU_QUES; default: return NONE_KEY; } } } uint8_t lang_get_shift_layer_number(void) { return lang_should_be * 2 + 1; } void lang_synchronize(void) { lang_timer = timer_read(); switch (lang_current_change) { case LANG_CHANGE_CAPS: { // Костыль, потому что при нажатии Shift+Caps включается режим Caps, а не переключение языка :facepalm: if (shift_current == 1) { unregister_code(KC_LSFT); register_code(KC_CAPS); unregister_code(KC_CAPS); register_code(KC_LSFT); } else { register_code(KC_CAPS); unregister_code(KC_CAPS); } } break; case LANG_CHANGE_ALT_SHIFT: { register_code(KC_LALT); register_code(KC_LSFT); unregister_code(KC_LSFT); unregister_code(KC_LALT); // Костыль, потому что при зажатом шифте если хочется нажать клавишу, которая переключает язык, то шифт слетает... if (shift_current == 1) { register_code(KC_LSFT); } } break; case LANG_CHANGE_SHIFT_ALT: { register_code(KC_LSFT); register_code(KC_LALT); unregister_code(KC_LALT); unregister_code(KC_LSFT); // Костыль, потому что при зажатом шифте если хочется нажать клавишу, которая переключает язык, то шифт слетает... if (shift_current == 1) { register_code(KC_LSFT); } } break; case LANG_CHANGE_CTRL_SHIFT: { register_code(KC_LCTL); register_code(KC_LSFT); unregister_code(KC_LSFT); unregister_code(KC_LCTL); // Костыль, потому что при зажатом шифте если хочется нажать клавишу, которая переключает язык, то шифт слетает... if (shift_current == 1) { register_code(KC_LSFT); } } break; case LANG_CHANGE_SHIFT_CTRL: { register_code(KC_LSFT); register_code(KC_LCTL); unregister_code(KC_LCTL); unregister_code(KC_LSFT); // Костыль, потому что при зажатом шифте если хочется нажать клавишу, которая переключает язык, то шифт слетает... if (shift_current == 1) { register_code(KC_LSFT); } } break; case LANG_CHANGE_WIN_SPACE: { register_code(KC_LGUI); register_code(KC_SPACE); unregister_code(KC_SPACE); unregister_code(KC_LGUI); } break; } } void lang_activate(Lang lang) { // Нужно дополнять этот код, если нужно три языка и более if (lang_current != lang) { lang_synchronize(); } lang_current = lang; } void lang_activate_from_user(Lang lang) { lang_should_be = lang; lang_activate(lang); } void lang_activate_from_user_without_sync(Lang lang) { lang_should_be = lang; lang_current = lang; } Key lang_process(Key key, bool down) { Key after_agnostic = lang_calc_agnostic(key); if (after_agnostic != NONE_KEY) { key = after_agnostic; } Lang new_lang = lang_get_lang(key); if (down) { if (new_lang != NONE_LANG) { lang_activate(new_lang); } else { lang_activate(lang_should_be); } } if (new_lang != NONE_LANG) { if (down) { lang_pressed_count++; } else { lang_pressed_count--; } } return lang_get_key(key); } void lang_user_timer(void) { // Нужно выключать язык после прохождения определённого времени, потому что пользователь ожидает как будто шифт на самом деле включён if (lang_pressed_count == 0 && lang_current != lang_should_be && timer_read() - lang_timer >= 100) { lang_activate(lang_should_be); } } // --------------------------------------------------------------------------- // Обработка клавиш // --------------------------------------------------------------------------- uint8_t lang_shift_current_shift_layer = 0; void lang_shift_press_key(Key key, bool down) { keyrecord_t record = { .event = { .key = { .col = 0, .row = 0, }, .pressed = down, .time = timer_read(), }, }; lang_shift_process_record(key, &record); } void lang_shift_tap_key(Key key) { lang_shift_press_key(key, true); lang_shift_press_key(key, false); shift_activate(shift_should_be); lang_activate(lang_should_be); } bool lang_shift_process_custom_keycodes(Key key, keyrecord_t* record) { bool down = record->event.pressed; // Обрабатываем клавиши, связанные с кастомным шифтом и кастомным переключением языка switch (key) { case SFT_N_O: shift_once_process_key(lang_get_shift_layer_number(), down); return false; case SFT_N: if (down) { shift_activate_from_user(true); lang_shift_current_shift_layer = lang_get_shift_layer_number(); layer_on(lang_shift_current_shift_layer); } else { shift_should_be = false; if (shift_pressed_count == 0) { shift_activate_from_user(false); } layer_off(lang_shift_current_shift_layer); } return false; case LA_CHNG: if (down) { if (lang_should_be == 0) { lang_activate_from_user(1); layer_on(2); } else { lang_activate_from_user(0); layer_off(2); } } return false; case LA_SYNC: if (down) { lang_synchronize(); } return false; case LA_CAPS: if (down) { lang_current_change = LANG_CHANGE_CAPS; } return false; case LA_ALSH: if (down) { lang_current_change = LANG_CHANGE_ALT_SHIFT; } return false; case LA_SHAL: if (down) { lang_current_change = LANG_CHANGE_SHIFT_ALT; } return false; case LA_CTSH: if (down) { lang_current_change = LANG_CHANGE_CTRL_SHIFT; } return false; case LA_SHCT: if (down) { lang_current_change = LANG_CHANGE_SHIFT_CTRL; } return false; case LA_WISP: if (down) { lang_current_change = LANG_CHANGE_WIN_SPACE; } return false; case AG_3DOT: if (record->event.pressed) { lang_shift_tap_key(AG_DOT); lang_shift_tap_key(AG_DOT); lang_shift_tap_key(AG_DOT); } return false; break; case AG_CMSP: if (record->event.pressed) { lang_shift_tap_key(AG_COMM); register_code(KC_SPC); unregister_code(KC_SPC); } return false; break; case AG_SDOT: if (record->event.pressed) { lang_shift_tap_key(AG_DOT); register_code(KC_SPC); unregister_code(KC_SPC); shift_once_use_to_next_key(lang_get_shift_layer_number()); } return false; break; } return true; } bool lang_shift_process_english_modifiers(Key key, keyrecord_t* record) { static Lang lang_stack[3] = {}; static uint8_t modifiers_count = 0; #define PROCESS(NAME, REGISTER, UNREGISTER, ACTIVATE_LANG) \ case NAME: { \ if (record->event.pressed) { \ lang_stack[modifiers_count] = lang_should_be; \ modifiers_count += 1; \ if (lang_should_be == 1) { \ layer_off(2); \ } \ if (ACTIVATE_LANG) { \ lang_activate_from_user(0); \ } else { \ lang_activate_from_user_without_sync(0); \ } \ REGISTER; \ } else { \ UNREGISTER; \ modifiers_count -= 1; \ if (ACTIVATE_LANG) { \ lang_activate_from_user(lang_stack[modifiers_count]); \ } else { \ lang_activate_from_user_without_sync(lang_stack[modifiers_count]); \ } \ if (lang_should_be == 1) { \ layer_on(2); \ } \ } \ return false; \ } break; #define Rg(x) register_code(KC_L ## x) #define Un(x) unregister_code(KC_L ## x) switch (key) { PROCESS(CTRL_0, Rg(CTL), Un(CTL), false); PROCESS(ALT_0, Rg(ALT), Un(ALT), false); PROCESS(WIN_0, Rg(GUI), Un(GUI), false); PROCESS(CTAL_0, { Rg(CTL); Rg(ALT); }, { Un(ALT); Un(CTL); }, false); PROCESS(SHAL_0, { Rg(SFT); Rg(ALT); }, { Un(ALT); Un(SFT); }, false); PROCESS(CTSH_0, { Rg(CTL); Rg(SFT); }, { Un(SFT); Un(CTL); }, false); PROCESS(MCAS_0, { Rg(CTL); Rg(ALT); Rg(SFT); }, { Un(SFT); Un(ALT); Un(CTL); }, false); PROCESS(CTRL_EN, Rg(CTL), Un(CTL), true); PROCESS(ALT_EN, Rg(ALT), Un(ALT), true); PROCESS(WIN_EN, Rg(GUI), Un(GUI), true); PROCESS(CTAL_EN, { Rg(CTL); Rg(ALT); }, { Un(ALT); Un(CTL); }, true); PROCESS(SHAL_EN, { Rg(SFT); Rg(ALT); }, { Un(ALT); Un(SFT); }, true); PROCESS(CTSH_EN, { Rg(CTL); Rg(SFT); }, { Un(SFT); Un(CTL); }, true); PROCESS(MCAS_EN, { Rg(CTL); Rg(ALT); Rg(SFT); }, { Un(SFT); Un(ALT); Un(CTL); }, true); } return true; } bool lang_shift_process_record(Key key, keyrecord_t* record) { // Обрабатываем Once Shift shift_once_process(key, record); bool down = record->event.pressed; // Разбираемся, имеет ли эта клавиша какой-то язык, заданный в ней Key key1 = lang_process(key, down); Key key_to_shift = key; if (key1 != NONE_KEY) { key_to_shift = key1; } // Разбираемся, имеет ли эта клавиша шифт, засунутый в неё // Это нужно отдельно от обработки языка, чтобы шифт мог выключаться для обычных клавиш Key key2 = shift_process(key_to_shift, down); if (key2 != NONE_KEY) { if (down) { register_code(key2); } else { unregister_code(key2); } return false; } if (!lang_shift_process_custom_keycodes(key, record)) { return false; } if (!lang_shift_process_english_modifiers(key, record)) { return false; } return true; } void lang_shift_user_timer(void) { shift_user_timer(); shift_once_user_timer(); lang_user_timer(); }