summaryrefslogtreecommitdiffstats
path: root/keyboards/contra
diff options
context:
space:
mode:
authorQMK Bot <hello@qmk.fm>2022-07-30 03:02:29 +0000
committerQMK Bot <hello@qmk.fm>2022-07-30 03:02:29 +0000
commit07f007de9ad23363e4d97367af11c7eaf078432f (patch)
tree50529b02b561c14cf4b7d8ddef69f579538db8d1 /keyboards/contra
parenta97c61074e93e916503488937ccc786b47f8e755 (diff)
parentd1096ad440e046fb44d3ba75c0049065b1dadb60 (diff)
Merge remote-tracking branch 'origin/master' into develop
Diffstat (limited to 'keyboards/contra')
-rwxr-xr-xkeyboards/contra/config.h8
-rw-r--r--keyboards/contra/keymaps/enigma/config.h27
-rw-r--r--keyboards/contra/keymaps/enigma/keymap.c625
-rw-r--r--keyboards/contra/keymaps/enigma/readme.md35
-rw-r--r--keyboards/contra/keymaps/enigma/rules.mk2
5 files changed, 689 insertions, 8 deletions
diff --git a/keyboards/contra/config.h b/keyboards/contra/config.h
index 5a21752e1f..7e4fddb59f 100755
--- a/keyboards/contra/config.h
+++ b/keyboards/contra/config.h
@@ -28,11 +28,3 @@
/* Locking resynchronize hack */
#define LOCKING_RESYNC_ENABLE
-
-#ifdef RGB_DI_PIN
-#define RGBLIGHT_ANIMATIONS
-#define RGBLED_NUM 0
-#define RGBLIGHT_HUE_STEP 8
-#define RGBLIGHT_SAT_STEP 8
-#define RGBLIGHT_VAL_STEP 8
-#endif
diff --git a/keyboards/contra/keymaps/enigma/config.h b/keyboards/contra/keymaps/enigma/config.h
new file mode 100644
index 0000000000..87f0199876
--- /dev/null
+++ b/keyboards/contra/keymaps/enigma/config.h
@@ -0,0 +1,27 @@
+/* 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/>.
+ */
+
+#pragma once
+
+#define RGB_DI_PIN F7
+#ifdef RGB_DI_PIN
+ #define RGBLIGHT_EFFECT_RAINBOW_SWIRL
+ #define RGBLIGHT_EFFECT_SNAKE
+ #define RGBLED_NUM 12
+ #define RGBLIGHT_HUE_STEP 8
+ #define RGBLIGHT_SAT_STEP 8
+ #define RGBLIGHT_VAL_STEP 8
+#endif \ No newline at end of file
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);
+}
diff --git a/keyboards/contra/keymaps/enigma/readme.md b/keyboards/contra/keymaps/enigma/readme.md
new file mode 100644
index 0000000000..9bc69b3980
--- /dev/null
+++ b/keyboards/contra/keymaps/enigma/readme.md
@@ -0,0 +1,35 @@
+# A Contra Layout with an Enigma Emulator
+
+```
+
+,-----------------------------------------------------------------------------------.
+| 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 |
+`-----------------------------------------------------------------------------------'
+```
+
+By default, this layout functions like a normal QWERTY layout. But it also has an in-built Enigma machine emulator.
+To enable the emulator, press `Fn+.`, and to go back to QWERTY, press `FN+,`.
+
+A demonstration video can be found [here](https://youtu.be/p8kBjP1DCzo).
+
+When Enigma mode is enabled, all letters A-Z will be enciphered using the Enigma emulator. Other keycodes will not be modified. Shift will be sent as normal, so you can choose whether to capitalize the output. If any other modifier (Ctrl, Alt, or Gui) are being pressed, the normal letter will come through.
+
+There are a few key combinations for configuring and diagnosing the Enigma emulator:
+
+* `Fn+Q` resets the emulator to the last-configured settings
+* `Fn+W` types out the current Enigma settings, e.g. `B. IA-IIA-IIIA (A, A, A) QW` (meaning reflector B; rotors I, II, and III all at ring setting A; all at position A, and with Q and W swapped on the plugboard)
+* `Fn+P` types out 1000 enciphered As, for testing
+* `Fn+Backspace` back-rotates the rotors one step, for fixing typos without having to re-type a whole message
+* `Fn+Z` followed by A, B, or C is used to set the reflector
+* `Fn+X` followed by three characters A-E is used to set the rotors
+* `Fn+C` followed by three characters A-Z is used to set the rotor positions
+* `Fn+V` followed by three characters A-Z is used to set the ring settings
+* `Fn+B` followed by up to 13 pairs of characters A-Z (one at a time, not simulaneously) then the Enter key is used to set the plugboard
+
+
diff --git a/keyboards/contra/keymaps/enigma/rules.mk b/keyboards/contra/keymaps/enigma/rules.mk
new file mode 100644
index 0000000000..261e59922a
--- /dev/null
+++ b/keyboards/contra/keymaps/enigma/rules.mk
@@ -0,0 +1,2 @@
+RGBLIGHT_ENABLE = yes
+RGBLIGHT_SUPPORTED = yes \ No newline at end of file