diff options
author | Chris Swenson <christopher@swenson.org> | 2022-07-29 22:01:35 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-07-29 20:01:35 -0700 |
commit | d1096ad440e046fb44d3ba75c0049065b1dadb60 (patch) | |
tree | bbe5e5a20fee2f861553a8d1484f695050fa7b38 /keyboards/contra/keymaps/enigma/keymap.c | |
parent | d2aa8e6bb8171c4af82bce678f098cd38597863e (diff) |
[Keymap] Add enigma keymap for contra keyboard (#17829)
Diffstat (limited to 'keyboards/contra/keymaps/enigma/keymap.c')
-rw-r--r-- | keyboards/contra/keymaps/enigma/keymap.c | 625 |
1 files changed, 625 insertions, 0 deletions
diff --git a/keyboards/contra/keymaps/enigma/keymap.c b/keyboards/contra/keymaps/enigma/keymap.c new file mode 100644 index 0000000000..d8779fb063 --- /dev/null +++ b/keyboards/contra/keymaps/enigma/keymap.c @@ -0,0 +1,625 @@ +/* Copyright 2022 Christopher Swenson + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include QMK_KEYBOARD_H + +enum planck_layers { + _ENIGMA, + _QWERTY, + _FN, + _LOWER, + _RAISE +}; + +enum planck_normal_keycodes { + QWERTY = SAFE_RANGE, + ENIGMA, + EN_A, + EN_B, + EN_C, + EN_D, + EN_E, + EN_F, + EN_G, + EN_H, + EN_I, + EN_J, + EN_K, + EN_L, + EN_M, + EN_N, + EN_O, + EN_P, + EN_Q, + EN_R, + EN_S, + EN_T, + EN_U, + EN_V, + EN_W, + EN_X, + EN_Y, + EN_Z, + EN_RES, + EN_TEST, + EN_DIAG, + EN_BSPC, + EN_SREF, + EN_SROT, + EN_SPOS, + EN_SRIN, + EN_SPLU +}; + +const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { + /* Enigma + * ,-----------------------------------------------------------------------------------. + * | Esc | Q | W | E | R | T | Y | U | I | O | P | Bksp | + * |------+------+------+------+------+-------------+------+------+------+------+------| + * | Tab | A | S | D | F | G | H | J | K | L | ; | " | + * |------+------+------+------+------+------|------+------+------+------+------+------| + * | Shift| Z | X | C | V | B | N | M | , | . | / |Enter | + * |------+------+------+------+------+------+------+------+------+------+------+------| + * | Ctrl | Fn | Alt | GUI |Lower | Space |Raise | Left | Down | Up |Right | + * `-----------------------------------------------------------------------------------' + */ + [_ENIGMA] = LAYOUT_planck_mit( + QK_GESC, EN_Q, EN_W, EN_E, EN_R, EN_T, EN_Y, EN_U, EN_I, EN_O, EN_P, KC_BSPC, + KC_TAB, EN_A, EN_S, EN_D, EN_F, EN_G, EN_H, EN_J, EN_K, EN_L, KC_SCLN, KC_QUOT, + KC_LSFT, EN_Z, EN_X, EN_C, EN_V, EN_B, EN_N, EN_M, KC_COMM, KC_DOT, KC_SLSH, KC_ENT, + KC_LCTL, MO(_FN), KC_LGUI, KC_LALT, MO(_LOWER), KC_SPC, MO(_RAISE), KC_LEFT, KC_DOWN, KC_UP, KC_RGHT + ), + /* Qwerty + * ,-----------------------------------------------------------------------------------. + * | Esc | Q | W | E | R | T | Y | U | I | O | P | Bksp | + * |------+------+------+------+------+-------------+------+------+------+------+------| + * | Tab | A | S | D | F | G | H | J | K | L | ; | " | + * |------+------+------+------+------+------|------+------+------+------+------+------| + * | Shift| Z | X | C | V | B | N | M | , | . | / |Enter | + * |------+------+------+------+------+------+------+------+------+------+------+------| + * | Ctrl | Fn | Alt | GUI |Lower | Space |Raise | Left | Down | Up |Right | + * `-----------------------------------------------------------------------------------' + */ + [_QWERTY] = LAYOUT_planck_mit( + QK_GESC, KC_Q, KC_W, KC_E, KC_R, KC_T, KC_Y, KC_U, KC_I, KC_O, KC_P, KC_BSPC, + KC_TAB, KC_A, KC_S, KC_D, KC_F, KC_G, KC_H, KC_J, KC_K, KC_L, KC_SCLN, KC_QUOT, + KC_LSFT, KC_Z, KC_X, KC_C, KC_V, KC_B, KC_N, KC_M, KC_COMM, KC_DOT, KC_SLSH, KC_ENT, + KC_LCTL, MO(_FN), KC_LGUI, KC_LALT, MO(_LOWER), KC_SPC, MO(_RAISE), KC_LEFT, KC_DOWN, KC_UP, KC_RGHT + ), + /* Function + * ,-----------------------------------------------------------------------------------. + * | Boot | Reset| Diag | | | | | | | | Test |Revers| + * |------+------+------+------+------+-------------+------+------+------+------+------| + * | | | | | | | | | | | | | + * |------+------+------+------+------+------|------+------+------+------+------+------| + * | |Reflec|Rotors|Posn.s|Rings |Plugs | | |Enigma|Qwerty| | | + * |------+------+------+------+------+------+------+------+------+------+------+------| + * | | | | | | | | | | | | + * `-----------------------------------------------------------------------------------' + */ + [_FN] = LAYOUT_planck_mit( + QK_BOOT, EN_RES, EN_DIAG, _______, _______, _______, _______, _______, _______, _______, EN_TEST, EN_BSPC, + _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, + _______, EN_SREF, EN_SROT, EN_SPOS, EN_SRIN, EN_SPLU, _______, _______, QWERTY, ENIGMA, _______, _______, + _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______ + ), + /* Lower + * ,-----------------------------------------------------------------------------------. + * | ~ | ! | @ | # | $ | % | ^ | & | * | ( | ) | Bksp | + * |------+------+------+------+------+-------------+------+------+------+------+------| + * | | | | | | | | _ | + | { | } | | | + * |------+------+------+------+------+------|------+------+------+------+------+------| + * | Shift| | | | | | | | | | | | + * |------+------+------+------+------+------+------+------+------+------+------+------| + * | Ctrl | Fn | Alt | GUI |Lower | Space |Raise | Left | Down | Up |Right | + * `-----------------------------------------------------------------------------------' + */ + [_LOWER] = LAYOUT_planck_mit( + KC_TILD, KC_EXLM, KC_AT, KC_HASH, KC_DLR, KC_PERC, KC_CIRC, KC_AMPR, KC_ASTR, KC_LPRN, KC_RPRN, KC_BSPC, + _______, _______, _______, _______, _______, _______, _______, KC_UNDS, KC_PLUS, KC_LCBR, KC_RCBR, KC_PIPE, + KC_LSFT, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, + KC_LCTL, _______, KC_LGUI, KC_LALT, _______, KC_SPC, _______, KC_LEFT, KC_DOWN, KC_UP, KC_RGHT + ), + /* RAISE + * ,-----------------------------------------------------------------------------------. + * | ` | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 0 | Bksp | + * |------+------+------+------+------+-------------+------+------+------+------+------| + * | | | | | | | | - | = | [ | ] | \ | + * |------+------+------+------+------+------|------+------+------+------+------+------| + * | Shift| | | | | | | | | | | | + * |------+------+------+------+------+------+------+------+------+------+------+------| + * | Ctrl | Fn | Alt | GUI |Lower | Space |Raise | Left | Down | Up |Right | + * `-----------------------------------------------------------------------------------' + */ + [_RAISE] = LAYOUT_planck_mit( + KC_GRV, KC_1, KC_2, KC_3, KC_4, KC_5, KC_6, KC_7, KC_8, KC_9, KC_0, KC_BSPC, + _______, _______, _______, _______, _______, _______, _______, KC_MINS, KC_EQL, KC_LBRC, KC_RBRC, KC_BSLS, + KC_LSFT, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, + KC_LCTL, _______, KC_LGUI, KC_LALT, _______, KC_SPC, _______, KC_LEFT, KC_DOWN, KC_UP, KC_RGHT + ), +}; + +char rotor_definitions[5][26] = { + "EKMFLGDQVZNTOWYHXUSPAIBRCJ", + "AJDKSIRUXBLHWTMCQGZNPYFVOE", + "BDFHJLCPRTXVZNYEIWGAKMUSQO", + "ESOVPZJAYQUIRHXLNFTGKDCMWB", + "VZBRGITYUPSDNHLXAWMJQOFECK" +}; + +char reflector_definitions[3][26] = { + "EJMZALYXVBWFCRQUONTSPIKHGD", + "YRUHQSLDPXNGOKMIEBFZCWVJAT", + "FVPJIAOYEDRZXWGCTKUQSBNMHL" +}; + +char notch[5] = "QEVJZ"; + +typedef struct Settings { + char rotor_order[3]; + char rotor_rings[3]; + char rotor_positions[3]; + char plugs[25]; + int plug_count; + char reflector; +} Settings; + +typedef struct KeyboardState { + bool is_setting_reflector; + bool is_setting_rotors; + bool is_setting_rotor_positions; + bool is_setting_rotor_rings; + bool is_setting_plugs; + char setting_progress[26]; + int setting_index; + Settings current_settings; + Settings default_settings; +} KeyboardState; + +int bound(int letter) { + return ((letter % 26) + 26) % 26; +} + +char to_char(int letter) { + return 'A' + letter; +} + +int to_int(char letter) { + return letter - 'A'; +} + +char encipher(char letter, Settings *settings) { + int rotor_2_step = settings->rotor_positions[2] == notch[settings->rotor_order[2] - 1]; + int rotor_1_step = settings->rotor_positions[1] == notch[settings->rotor_order[1] - 1]; + + // Advance the third rotor + settings->rotor_positions[2]++; + if (settings->rotor_positions[2] > 'Z') { + settings->rotor_positions[2] -= 26; + } + + // Maybe advance the second rotor, including double steps + if (rotor_2_step || rotor_1_step) { + settings->rotor_positions[1]++; + if (settings->rotor_positions[1] > 'Z') { + settings->rotor_positions[1] -= 26; + } + } + + // Maybe advance the first rotor + if (rotor_1_step) { + settings->rotor_positions[0]++; + if (settings->rotor_positions[0] > 'Z') { + settings->rotor_positions[0] -= 26; + } + } + + // Swap letters on plugboard + for (int i = 0; i < settings->plug_count * 2; i += 2) { + if (letter == settings->plugs[i]) { + letter = settings->plugs[i + 1]; + } else if (letter == settings->plugs[i + 1]) { + letter = settings->plugs[i]; + } + } + + // Rotors (right to left) + for (int rotor_index = 2; rotor_index >= 0; rotor_index--) { + char *rotor_definition = rotor_definitions[settings->rotor_order[rotor_index] - 1]; + int position = to_int(settings->rotor_positions[rotor_index]); + int ring = to_int(settings->rotor_rings[rotor_index]); + int char_index = to_int(letter); + letter = to_char(bound( + to_int(rotor_definition[bound(char_index + position - ring)]) + ring - position + )); + } + + // Swap via reflector + letter = reflector_definitions[to_int(settings->reflector)][to_int(letter)]; + + // Rotors in reverse (left to right) + for (int rotor_index = 0; rotor_index < 3; rotor_index++) { + char *rotor_definition = rotor_definitions[settings->rotor_order[rotor_index] - 1]; + int position = to_int(settings->rotor_positions[rotor_index]); + int ring = to_int(settings->rotor_rings[rotor_index]); + int search_index; + for (search_index = 0; search_index < 26; search_index++) { + if ( + rotor_definition[search_index] + == to_char(bound(to_int(letter) - ring + position)) + ) { + break; + } + } + letter = to_char(bound(search_index - position + ring)); + } + + // Plugboard again + for (int i = 0; i < settings->plug_count * 2; i += 2) { + if (letter == settings->plugs[i]) { + letter = settings->plugs[i + 1]; + } else if (letter == settings->plugs[i + 1]) { + letter = settings->plugs[i]; + } + } + + return letter; +} + +void init_enigma_default(Settings *settings) { + settings->rotor_order[0] = 1; + settings->rotor_rings[0] = 'A'; + settings->rotor_positions[0] = 'A'; + + settings->rotor_order[1] = 2; + settings->rotor_rings[1] = 'A'; + settings->rotor_positions[1] = 'A'; + + settings->rotor_order[2] = 3; + settings->rotor_rings[2] = 'A'; + settings->rotor_positions[2] = 'A'; + + strcpy(settings->plugs, ""); + settings->plug_count = 0; + + settings->reflector = 'B'; +} + +void copy_settings(Settings *from, Settings *to) { + to->rotor_order[0] = from->rotor_order[0]; + to->rotor_rings[0] = from->rotor_rings[0]; + to->rotor_positions[0] = from->rotor_positions[0]; + + to->rotor_order[1] = from->rotor_order[1]; + to->rotor_rings[1] = from->rotor_rings[1]; + to->rotor_positions[1] = from->rotor_positions[1]; + + to->rotor_order[2] = from->rotor_order[2]; + to->rotor_rings[2] = from->rotor_rings[2]; + to->rotor_positions[2] = from->rotor_positions[2]; + + strncpy(to->plugs, from->plugs, from->plug_count * 2); + to->plug_count = from->plug_count; + + to->reflector = from->reflector; +} + +char *rotor_name(int rotor_number) { + if (rotor_number == 1) { + return "I"; + } else if (rotor_number == 2) { + return "II"; + } else if (rotor_number == 3) { + return "III"; + } else if (rotor_number == 4) { + return "IV"; + } else if (rotor_number == 5) { + return "V"; + } + return "?"; +} + +void rotors_reverse(Settings *settings) { + int rotor_2_step = settings->rotor_positions[2] + == to_char(bound(to_int(notch[settings->rotor_order[2] - 1]) + 1)); + int rotor_1_step = settings->rotor_positions[1] + == to_char(bound(to_int(notch[settings->rotor_order[1] - 1]) + 1)); + + // Reverse third rotor + settings->rotor_positions[2]--; + if (settings->rotor_positions[2] < 'A') { + settings->rotor_positions[2] += 26; + } + + // Maybe reverse second rotor (including double steps) + if (rotor_2_step || rotor_1_step) { + settings->rotor_positions[1]--; + if (settings->rotor_positions[1] < 'A') { + settings->rotor_positions[1] += 26; + } + } + + // Maybe recerse first rotor + if (rotor_1_step) { + settings->rotor_positions[0]--; + if (settings->rotor_positions[0] < 'A') { + settings->rotor_positions[0] += 26; + } + } +} + +void reset_settings(KeyboardState *state) { + copy_settings(&state->default_settings, &state->current_settings); +} + +void set_layer(uint8_t default_layer) { + default_layer_set((layer_state_t)1 << default_layer); +} + +void set_backlight(uint8_t mode, uint8_t hue, uint8_t sat, uint8_t val) { + #ifdef RGBLIGHT_ENABLE + rgblight_enable_noeeprom(); + rgblight_mode_noeeprom(mode); + rgblight_sethsv_noeeprom(hue, sat, val); + #endif +} + +void clear_working_settings(KeyboardState *state) { + state->is_setting_reflector = false; + state->is_setting_rotors = false; + state->is_setting_rotor_positions = false; + state->is_setting_rotor_rings = false; + state->is_setting_plugs = false; + state->setting_index = 0; + set_layer(_ENIGMA); + set_backlight(RGBLIGHT_MODE_STATIC_LIGHT, HSV_RED); +} + +void send_settings_string(Settings *settings) { + send_char(settings->reflector); + send_string(". "); + send_string(rotor_name(settings->rotor_order[0])); + send_char(settings->rotor_rings[0]); + send_string("-"); + send_string(rotor_name(settings->rotor_order[1])); + send_char(settings->rotor_rings[1]); + send_string("-"); + send_string(rotor_name(settings->rotor_order[2])); + send_char(settings->rotor_rings[2]); + send_string(" ("); + send_char(settings->rotor_positions[0]); + send_string(", "); + send_char(settings->rotor_positions[1]); + send_string(", "); + send_char(settings->rotor_positions[2]); + send_string(") "); + for (int i = 0; i < settings->plug_count; i++) { + send_char(settings->plugs[i * 2]); + send_char(settings->plugs[i * 2 + 1]); + send_string(" "); + } + send_string("\n"); +} + +void perform_test(Settings *settings) { + for (int i = 0; i < 1000; i++) { + send_char(encipher('A', settings) - ('A' - 'a')); + } +} + +void commit_plug_settings(KeyboardState *state) { + state->default_settings.plug_count = state->setting_index / 2; + for (int i = 0; i < state->default_settings.plug_count; i++) { + state->default_settings.plugs[i * 2] = state->setting_progress[i * 2]; + state->default_settings.plugs[i * 2 + 1] = state->setting_progress[i * 2 + 1]; + } + reset_settings(state); + clear_working_settings(state); +} + +void handle_set_reflector(char letter, KeyboardState *state) { + if (letter >= 'A' && letter <= 'C') { + state->default_settings.reflector = letter; + reset_settings(state); + clear_working_settings(state); + } +} + +bool handle_set_rotor(char letter, KeyboardState *state) { + bool is_valid = letter >= 'A' && letter <= 'E' && state->setting_index < 3; + if (is_valid) { + state->setting_progress[state->setting_index] = to_int(letter) + 1; + state->setting_index += 1; + if (state->setting_index == 3) { + state->default_settings.rotor_order[0] = state->setting_progress[0]; + state->default_settings.rotor_order[1] = state->setting_progress[1]; + state->default_settings.rotor_order[2] = state->setting_progress[2]; + reset_settings(state); + clear_working_settings(state); + } + } + return is_valid; +} + +bool handle_set_rotor_position(char letter, KeyboardState *state) { + bool is_valid = state->setting_index < 3; // Guaranteed to be A-Z already + if (is_valid) { + state->setting_progress[state->setting_index] = letter; + state->setting_index += 1; + if (state->setting_index == 3) { + state->default_settings.rotor_positions[0] = state->setting_progress[0]; + state->default_settings.rotor_positions[1] = state->setting_progress[1]; + state->default_settings.rotor_positions[2] = state->setting_progress[2]; + reset_settings(state); + clear_working_settings(state); + } + } + return is_valid; +} + +bool handle_set_rotor_ring(char letter, KeyboardState *state) { + bool is_valid = state->setting_index < 3; + if (is_valid) { + state->setting_progress[state->setting_index] = letter; + state->setting_index += 1; + if (state->setting_index == 3) { + state->default_settings.rotor_rings[0] = state->setting_progress[0]; + state->default_settings.rotor_rings[1] = state->setting_progress[1]; + state->default_settings.rotor_rings[2] = state->setting_progress[2]; + reset_settings(state); + clear_working_settings(state); + } + } + return is_valid; +} + +bool handle_set_plug(char letter, KeyboardState *state) { + bool is_valid = state->setting_index < 26; + if (is_valid) { + state->setting_progress[state->setting_index] = letter; + state->setting_index += 1; + } + return is_valid; +} + +void handle_enigma_keypress(char letter, bool any_mods, KeyboardState *state) { + bool settings_valid = true; + if (letter) { + if (any_mods) { + tap_code(KC_A + to_int(letter)); + } else if (state->is_setting_reflector) { + handle_set_reflector(letter, state); + } else if (state->is_setting_rotors) { + settings_valid = handle_set_rotor(letter, state); + } else if (state->is_setting_rotor_positions) { + settings_valid = handle_set_rotor_position(letter, state); + } else if (state->is_setting_rotor_rings) { + settings_valid = handle_set_rotor_ring(letter, state); + } else if (state->is_setting_plugs) { + settings_valid = handle_set_plug(letter, state); + } else { + char c2 = encipher(letter, &state->current_settings); + send_char(c2 - ('A' - 'a')); + } + } + if (!settings_valid) { + clear_working_settings(state); + } +} + +KeyboardState STATE; + +void keyboard_pre_init_user(void) { + init_enigma_default(&STATE.default_settings); + init_enigma_default(&STATE.current_settings); + clear_working_settings(&STATE); +} + +bool process_record_user(uint16_t keycode, keyrecord_t *record) { + uint8_t letter_index; + bool letter_found = false; + if (record->event.pressed) { + switch (keycode) { + case QWERTY: + set_layer(_QWERTY); + set_backlight(RGBLIGHT_MODE_RAINBOW_SWIRL + 4, HSV_PURPLE); + break; + case ENIGMA: + set_layer(_ENIGMA); + set_backlight(RGBLIGHT_MODE_STATIC_LIGHT, HSV_RED); + break; + case EN_SREF: + reset_settings(&STATE); + STATE.is_setting_reflector = true; + set_layer(_ENIGMA); + set_backlight(RGBLIGHT_MODE_SNAKE, HSV_RED); + break; + case EN_SROT: + reset_settings(&STATE); + STATE.is_setting_rotors = true; + STATE.setting_index = 0; + set_layer(_ENIGMA); + set_backlight(RGBLIGHT_MODE_SNAKE, 10, 255, 255); + break; + case EN_SPOS: + reset_settings(&STATE); + STATE.is_setting_rotor_positions = true; + STATE.setting_index = 0; + set_layer(_ENIGMA); + set_backlight(RGBLIGHT_MODE_SNAKE, HSV_ORANGE); + break; + case EN_SRIN: + reset_settings(&STATE); + STATE.is_setting_rotor_rings = true; + STATE.setting_index = 0; + set_layer(_ENIGMA); + set_backlight(RGBLIGHT_MODE_SNAKE, HSV_GREEN); + break; + case EN_SPLU: + reset_settings(&STATE); + STATE.is_setting_plugs = true; + STATE.setting_index = 0; + set_layer(_ENIGMA); + set_backlight(RGBLIGHT_MODE_SNAKE, HSV_BLUE); + break; + case QK_GESC: + if ( + STATE.is_setting_reflector + || STATE.is_setting_rotors + || STATE.is_setting_rotor_positions + || STATE.is_setting_rotor_rings + || STATE.is_setting_plugs + ) { + clear_working_settings(&STATE); + return false; + } + break; + case KC_ENT: + if (STATE.is_setting_plugs) { + commit_plug_settings(&STATE); + return false; + } + break; + case EN_A ... EN_Z: + letter_index = keycode - EN_A; + letter_found = true; + break; + case EN_RES: + reset_settings(&STATE); + break; + case EN_TEST: + perform_test(&STATE.current_settings); + break; + case EN_DIAG: + send_settings_string(&STATE.current_settings); + break; + case EN_BSPC: + rotors_reverse(&STATE.current_settings); + tap_code(KC_BSPC); + break; + } + } + char letter = letter_found ? 'A' + letter_index : 0; + uint8_t mods = get_mods(); + bool any_mods = (mods & MOD_MASK_CTRL) || (mods & MOD_MASK_ALT) || (mods & MOD_MASK_GUI); + handle_enigma_keypress(letter, any_mods, &STATE); + return true; +} + +void keyboard_post_init_user(void) { + set_layer(_QWERTY); + set_backlight(RGBLIGHT_MODE_RAINBOW_SWIRL + 4, HSV_PURPLE); +} |