From cd92946f492bf13e09c6a5e6d24dcc4bbc523e6a Mon Sep 17 00:00:00 2001
From: Sébastien Dailly <sebastien@dailly.me>
Date: Tue, 19 Dec 2023 21:44:34 +0100
Subject: Added the qmk firmware for sofle keyboard

---
 qmk/keyboards/sofle_choc/keymaps/custom/config.h   |  45 ++++
 qmk/keyboards/sofle_choc/keymaps/custom/keycodes.c |  78 +++++++
 qmk/keyboards/sofle_choc/keymaps/custom/keycodes.h |  28 +++
 qmk/keyboards/sofle_choc/keymaps/custom/keymap.c   |  68 +++++++
 .../sofle_choc/keymaps/custom/quad_tapdance.c      | 226 +++++++++++++++++++++
 .../sofle_choc/keymaps/custom/quad_tapdance.h      |  30 +++
 qmk/keyboards/sofle_choc/keymaps/custom/rules.mk   |   6 +
 7 files changed, 481 insertions(+)
 create mode 100644 qmk/keyboards/sofle_choc/keymaps/custom/config.h
 create mode 100644 qmk/keyboards/sofle_choc/keymaps/custom/keycodes.c
 create mode 100644 qmk/keyboards/sofle_choc/keymaps/custom/keycodes.h
 create mode 100644 qmk/keyboards/sofle_choc/keymaps/custom/keymap.c
 create mode 100644 qmk/keyboards/sofle_choc/keymaps/custom/quad_tapdance.c
 create mode 100644 qmk/keyboards/sofle_choc/keymaps/custom/quad_tapdance.h
 create mode 100644 qmk/keyboards/sofle_choc/keymaps/custom/rules.mk

(limited to 'qmk')

diff --git a/qmk/keyboards/sofle_choc/keymaps/custom/config.h b/qmk/keyboards/sofle_choc/keymaps/custom/config.h
new file mode 100644
index 0000000..9a89fd8
--- /dev/null
+++ b/qmk/keyboards/sofle_choc/keymaps/custom/config.h
@@ -0,0 +1,45 @@
+/* Copyright 2023 Brian Low
+ *
+ * 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
+
+// Enabling this option changes the startup behavior to listen for an
+// active USB communication to delegate which part is master and which
+// is slave. With this option enabled and theres’s USB communication,
+// then that half assumes it is the master, otherwise it assumes it
+// is the slave.
+//
+// I've found this helps with some ProMicros where the slave does not boot
+#define SPLIT_USB_DETECT
+
+#define RGB_DISABLE_WHEN_USB_SUSPENDED     // turn off effects when suspended
+#define SPLIT_TRANSPORT_MIRROR             // If LED_MATRIX_KEYPRESSES or LED_MATRIX_KEYRELEASES is enabled, you also will want to enable SPLIT_TRANSPORT_MIRROR
+#define RGB_MATRIX_MAXIMUM_BRIGHTNESS 100  // limits maximum brightness of LEDs (max 255). Higher may cause the controller to crash.
+
+
+
+#define RP2040_BOOTLOADER_DOUBLE_TAP_RESET // Activates the double-tap behavior
+#define RP2040_BOOTLOADER_DOUBLE_TAP_RESET_TIMEOUT 200U // Timeout window in ms in which the double tap can occur.
+#define RP2040_BOOTLOADER_DOUBLE_TAP_RESET_LED D5 // Specify an optional status LED by GPIO number which blinks when entering the bootloader
+
+#define TAPPING_TERM 200
+
+// Movihy the tap-hold configuration for helping with some tap-dance keys.
+// The behavior with double-tap (like c/ç) and mod-tap is to wait the delay for
+// the tap-dance before reporting the key to send.
+// If I release the MOD-TAP key before, the modifier is not applied and the
+// host receive a sequence of two keys, which is not what I want.
+//#define PERMISSIVE_HOLD
+//#define HOLD_ON_OTHER_KEY_PRESS
diff --git a/qmk/keyboards/sofle_choc/keymaps/custom/keycodes.c b/qmk/keyboards/sofle_choc/keymaps/custom/keycodes.c
new file mode 100644
index 0000000..e0f8994
--- /dev/null
+++ b/qmk/keyboards/sofle_choc/keymaps/custom/keycodes.c
@@ -0,0 +1,78 @@
+#include QMK_KEYBOARD_H
+#include "keymap_bepo.h"
+#include "keycodes.h"
+
+/*
+ * Rules and modifier to apply over the keycodes. This includes the keys
+ * redefinitions and the keys to include in the caps_word mecanism.
+ *
+ * All thoses update are working over the custom keys declared in `keycodes.h` 
+ */
+
+/*
+ * This function need to be declared after the key definition, including the
+ * tapdance keys because I need to reference the keycode here if I want to
+ * include them in the caps_word mecanism.
+ */
+bool caps_word_press_user(uint16_t keycode) {
+    switch (keycode) {
+        // Keycodes that continue Caps Word, with shift applied.
+        case KC_A ... KC_Z:
+        case KC_1 ... KC_0:
+        case KC_MINS:
+        case KEY_C: // Add also the tapdance keys here.
+        case KEY_W:
+        case KEY_E:
+        case BP_Z:  // Additionals keys from the bepo layout.
+        case BP_M:
+        case BP_G:
+        case BP_H:
+        case BP_N:
+            add_weak_mods(MOD_BIT(KC_LSFT));  // Apply shift to next key.
+            return true;
+
+        // Keycodes that continue Caps Word, without shifting.
+        case KC_BSPC:
+        case KC_DEL:
+            return true;
+        case KC_SPACE:
+            // The space key is used in order to generate the _ symbol,
+            // I check which modifier is applied, it’s ok when it’s ALT
+            return get_mods() & MOD_MASK_ALT;
+
+        default:
+            return false;  // Deactivate Caps Word.
+    }
+}
+
+//
+// Override the symbol ° and replace it by `
+// The symbol is still available in the symbol layer with the key just below.
+//
+const key_override_t perc_key_override = 
+    ko_make_basic(MOD_MASK_SHIFT, KEY_PRC, LSFT(BP_PERC));
+
+// 
+// I don’t care of the mapping CTRL+Ç and using the mod_tap does not work well
+// when I type too fast because of tap-dance, so I remap the pattern here.
+//
+const key_override_t c_key_override = 
+    ko_make_basic(MOD_MASK_CTRL, KEY_C, LCTL(BP_C));
+
+// Same here, I override the key W with CTRL because the tapdance activate the 
+// caps_word which does not make sense here.
+const key_override_t w_key_override = 
+    ko_make_basic(MOD_MASK_CTRL, KEY_W, LCTL(BP_W));
+
+const key_override_t e_key_override = 
+    ko_make_basic(MOD_MASK_CTRL, KEY_E, RCTL(BP_E));
+
+
+// This globally defines all key overrides to be used
+const key_override_t **key_overrides = (const key_override_t *[]){
+    &perc_key_override,
+    &c_key_override,
+    &w_key_override,
+    &e_key_override,
+    NULL
+};
diff --git a/qmk/keyboards/sofle_choc/keymaps/custom/keycodes.h b/qmk/keyboards/sofle_choc/keymaps/custom/keycodes.h
new file mode 100644
index 0000000..9837769
--- /dev/null
+++ b/qmk/keyboards/sofle_choc/keymaps/custom/keycodes.h
@@ -0,0 +1,28 @@
+#pragma once
+
+enum {
+  // Custom key for defining the tapdance allowing to transform C into Ç
+  TD_C_CCED,
+  // Transform the key W into SHIFT or CAPSLOCK
+  TD_W_CAPSLOCK,
+  // Transform the key E into È
+  TD_E_EE,
+  TD_PRC,
+  TD_LAYER_SYMB,
+  TD_LSFT,
+};
+
+#define _BASE 0
+#define LAYER_SYMBOLS 1
+
+#define MENU    LGUI(BP_I)
+#define KEY_C   TD(TD_C_CCED)
+#define KEY_W   TD(TD_W_CAPSLOCK)
+#define KEY_PRC TD(TD_PRC)
+#define LT_SFT  TD(TD_LSFT)
+#define AL_ENT  MT(MOD_RALT, KC_ENT)
+#define AL_SPC  MT(MOD_LALT, KC_SPC)
+#define KEY_E   TD(TD_E_EE)
+#define KEY_T   MT(MOD_RCTL, BP_T)
+#define KEY_INS MT(MOD_RGUI, KC_INS)
+
diff --git a/qmk/keyboards/sofle_choc/keymaps/custom/keymap.c b/qmk/keyboards/sofle_choc/keymaps/custom/keymap.c
new file mode 100644
index 0000000..fb1ba89
--- /dev/null
+++ b/qmk/keyboards/sofle_choc/keymaps/custom/keymap.c
@@ -0,0 +1,68 @@
+/* Copyright 2023 Brian Low
+ *
+ * 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
+#include "keymap_bepo.h"
+#include "keycodes.h"
+
+
+#define LY_SYMBOL TD(TD_LAYER_SYMB)
+
+
+const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
+/*
+ * BÉPO
+ * ,-----------------------------------------.                    ,-----------------------------------------.
+ * |  `   |   1  |   2  |   3  |   4  |   5  |                    |   6  |   7  |   8  |   9  |   0  |  °   |
+ * |------+------+------+------+------+------|                    |------+------+------+------+------+------|
+ * | ESC  |   B  |   É  |   P  |   O  |   È  |                    |   !  |   V  |   D  |   L  |   J  |  Z   |
+ * |------+------+------+------+------+------|                    |------+------+------+------+------+------|
+ * | Tab  |   A  |   U  |   I  |   EÈ |   ;  |-------.    ,-------|  CÇ  |   T  |   S  |   R  |   N  |  M   |
+ * |------+------+------+------+------+------|  Mute |    | Pause |------+------+------+------+------+------|
+ * |LShift|   À  |   Y  |   X  |   :  |   K  |-------|    |-------|   ?  |   Q  |   G  |   H  |   F  |  W   |
+ * `-----------------------------------------/       /     \      \-----------------------------------------'
+ *            | LCTL | LGUI | LCMD | Layer| / Space /       \Enter \  | Layer| RCMD | RGUI | RCTL |
+ *            |      |      |      |  dwn |/ LALT  /         \ RALT \ |   up |      |      |      |
+ *            `-----------------------------------'           '------''---------------------------'
+ */
+
+[_BASE] = LAYOUT(
+    KC_ESC,   KC_1,   KC_2,    KC_3,    KC_4,    KC_5,                       KC_6,     KC_7,     KC_8,    KC_9,    KC_0,    KEY_PRC,
+    BP_DLR,   BP_B,   BP_EACU, BP_P,    BP_O,    KC_BSPC,                    BP_DCIR,  BP_V,     BP_D,    BP_L,    BP_J,    BP_Z,
+    KC_TAB,   BP_A,   BP_U,    BP_I,    KEY_E,   BP_COMM,                    KEY_C,    KEY_T,    BP_S,    BP_R,    BP_N,    BP_M,
+    LT_SFT,   BP_AGRV,BP_Y,    BP_X,    BP_DOT,  KC_B,    KC_MUTE,   KC_MPLY,BP_QUOT, BP_Q,      BP_G,    BP_H,    BP_F,    KEY_W,
+                      KC_LCTL, KC_LGUI, KC_BSPC, LY_SYMBOL,AL_SPC,   AL_ENT, LY_SYMBOL,MENU,     KEY_INS, KC_RCTL
+),
+
+// In the number layout, I keep the mod-tap modifiers applies to the middle letters :
+//
+#define KEY_5   MT(MOD_LCTL, KC_P5)
+#define KEY_DOWN   MT(MOD_RCTL, KC_DOWN)
+
+[LAYER_SYMBOLS] = LAYOUT(
+    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_NO,    KC_NO,  KC_NO,   KC_P7,   KC_P8,   KC_P9,                      KC_HOME,  KC_UP,    KC_END,  KC_NO,   KC_NO,   S(BP_EQL),
+    KC_TAB,   KC_NO,  KC_NO,   KC_P4,   KEY_5,   KC_P6,                      KC_LEFT,  KEY_DOWN, KC_RIGHT,KC_NO,   KC_NO,   KC_NO,
+    LT_SFT,   KC_NO,  KC_P0,   KC_P1,   KC_P2,   KC_P3,   KC_MUTE,   KC_MPLY,KC_PGUP,  KC_NO,    KC_PGDN, KC_NO,   KC_NO,   KC_RIGHT_SHIFT,
+                      KC_LCTL, KC_LGUI, KC_BSPC, LY_SYMBOL,AL_SPC,   AL_ENT, LY_SYMBOL,KC_NO,    KC_RGUI, KC_RCTL
+),
+};
+
+#if defined(ENCODER_MAP_ENABLE)
+const uint16_t PROGMEM encoder_map[][NUM_ENCODERS][NUM_DIRECTIONS] = {
+    [_BASE] = { ENCODER_CCW_CW(KC_VOLD, KC_VOLU), ENCODER_CCW_CW(KC_MS_WH_UP, KC_MS_WH_DOWN) },
+    [LAYER_SYMBOLS] = { ENCODER_CCW_CW(KC_VOLD, KC_VOLU), ENCODER_CCW_CW(KC_MS_WH_UP, KC_MS_WH_DOWN) },
+};
+#endif
diff --git a/qmk/keyboards/sofle_choc/keymaps/custom/quad_tapdance.c b/qmk/keyboards/sofle_choc/keymaps/custom/quad_tapdance.c
new file mode 100644
index 0000000..9978315
--- /dev/null
+++ b/qmk/keyboards/sofle_choc/keymaps/custom/quad_tapdance.c
@@ -0,0 +1,226 @@
+/* Return an integer that corresponds to what kind of tap dance should be executed.
+ *
+ * How to figure out tap dance state: interrupted and pressed.
+ *
+ * Interrupted: If the state of a dance is "interrupted", that means that another key has been hit
+ *  under the tapping term. This is typically indicitive that you are trying to "tap" the key.
+ *
+ * Pressed: Whether or not the key is still being pressed. If this value is true, that means the tapping term
+ *  has ended, but the key is still being pressed down. This generally means the key is being "held".
+ *
+ * One thing that is currenlty not possible with qmk software in regards to tap dance is to mimic the "permissive hold"
+ *  feature. In general, advanced tap dances do not work well if they are used with commonly typed letters.
+ *  For example "A". Tap dances are best used on non-letter keys that are not hit while typing letters.
+ *
+ * Good places to put an advanced tap dance:
+ *  z,q,x,j,k,v,b, any function key, home/end, comma, semi-colon
+ *
+ * Criteria for "good placement" of a tap dance key:
+ *  Not a key that is hit frequently in a sentence
+ *  Not a key that is used frequently to double tap, for example 'tab' is often double tapped in a terminal, or
+ *    in a web form. So 'tab' would be a poor choice for a tap dance.
+ *  Letters used in common words as a double. For example 'p' in 'pepper'. If a tap dance function existed on the
+ *    letter 'p', the word 'pepper' would be quite frustating to type.
+ *
+ * For the third point, there does exist the 'TD_DOUBLE_SINGLE_TAP', however this is not fully tested
+ *
+ */
+
+#include QMK_KEYBOARD_H
+#include "keymap_bepo.h"
+#include "quad_tapdance.h"
+#include "keycodes.h"
+
+td_state_t cur_dance(tap_dance_state_t *state) {
+    if (state->count == 1) {
+        if (!state->pressed) return TD_SINGLE_TAP;
+        // Key has not been interrupted, but the key is still held. Means you want to send a 'HOLD'.
+        else return TD_SINGLE_HOLD;
+    } else if (state->count == 2) {
+        // TD_DOUBLE_SINGLE_TAP is to distinguish between typing "pepper", and actually wanting a double tap
+        // action when hitting 'pp'. Suggested use case for this return value is when you want to send two
+        // keystrokes of the key, and not the 'double tap' action/macro.
+        if (state->interrupted) return TD_DOUBLE_SINGLE_TAP;
+        else if (state->pressed) return TD_DOUBLE_HOLD;
+        else return TD_DOUBLE_TAP;
+    }
+
+    // Assumes no one is trying to type the same letter three times (at least not quickly).
+    // If your tap dance key is 'KC_W', and you want to type "www." quickly - then you will need to add
+    // an exception here to return a 'TD_TRIPLE_SINGLE_TAP', and define that enum just like 'TD_DOUBLE_SINGLE_TAP'
+    if (state->count == 3) {
+        if (state->interrupted || !state->pressed) return TD_TRIPLE_TAP;
+        else return TD_TRIPLE_HOLD;
+    } else return TD_UNKNOWN;
+}
+
+//
+// Definition for the key W.
+//
+// The main usage is to send the letter W when pressed, but the key is also
+// used to activate the SHIFT mode when pressed.
+//
+// If the key is double tapped, it will also switch into the caps_word mode.
+//
+
+static td_tap_t w_tap_state = {
+    .is_press_action = true,
+    .state = TD_NONE
+};
+
+void w_finished(tap_dance_state_t *state, void *user_data) {
+    w_tap_state.state = cur_dance(state);
+    switch (w_tap_state.state) {
+        case TD_SINGLE_TAP: register_code(BP_W); break;
+        case TD_SINGLE_HOLD: register_code(KC_RIGHT_SHIFT); break;
+        case TD_DOUBLE_TAP: caps_word_on(); break;
+        case TD_DOUBLE_SINGLE_TAP: tap_code(BP_W); register_code(BP_W); break;
+        default: break;
+    }
+}
+
+void w_reset(tap_dance_state_t *state, void *user_data) {
+    switch (w_tap_state.state) {
+        case TD_SINGLE_TAP: unregister_code(BP_W); break;
+        case TD_SINGLE_HOLD: unregister_code(KC_RIGHT_SHIFT); break;
+        case TD_DOUBLE_SINGLE_TAP: unregister_code(BP_W); break;
+        default: break;
+    }
+    w_tap_state.state = TD_NONE;
+}
+
+//
+// Definition for the layer key
+//
+// The only one usage is to activate the layer over the keyboard, but there is
+// two way of doing it:
+//
+// The first mode, when hold, is to activate the layer as long as the key is pressed.
+// The second one is to switch in the layer mode when double tapped, in this
+// mode, you can go back into the normal layer by a single press on the key.
+//
+static td_tap_t ql_tap_state = {
+    .is_press_action = true,
+    .state = TD_NONE
+};
+
+// Functions that control what our tap dance key does
+void ql_finished(tap_dance_state_t *state, void *user_data) {
+    ql_tap_state.state = cur_dance(state);
+    switch (ql_tap_state.state) {
+        // Remove the layer with a single tap, this way I always have a key to remove the
+        // the layer, without knowing the previous state I had.
+        case TD_SINGLE_TAP:  layer_off(LAYER_SYMBOLS); break;
+        case TD_SINGLE_HOLD: layer_on(LAYER_SYMBOLS);  break;
+        case TD_DOUBLE_TAP:  layer_invert(LAYER_SYMBOLS); break;
+        default: break;
+    }
+}
+
+void ql_reset(tap_dance_state_t *state, void *user_data) {
+    // If the key was held down and now is released then switch off the layer
+    if (ql_tap_state.state == TD_SINGLE_HOLD) {
+        layer_off(LAYER_SYMBOLS);
+    }
+    ql_tap_state.state = TD_NONE;
+}
+
+//
+// Definiton for the key DEL.
+// The main usage of the key is to activate the LShift when hold. But the key
+// can also be used to send the DEL keycode.
+//
+// As I do not want to send the DEL keycode by mistake, I’ve configured the key
+// to do it on double-tap. If you hold the key after a double tap, the DELETE
+// code will be repeated again until the key is released.
+//
+// On a single press, the key will just remove the CAPSLOCK state if active.
+//
+static td_tap_t lshift_tap_state = {
+    .is_press_action = true,
+    .state = TD_NONE
+};
+
+// Functions that control what our tap dance key does
+void lshift_finished(tap_dance_state_t *state, void *user_data) {
+    lshift_tap_state.state = cur_dance(state);
+    switch (lshift_tap_state.state) {
+        // Remove the layer with a single tap, this way I always have a key to remove the
+        // the layer, without knowing the previous state I had.
+        case TD_SINGLE_HOLD: register_code(KC_LEFT_SHIFT); break;
+        case TD_DOUBLE_TAP: 
+        case TD_DOUBLE_HOLD: 
+                             register_code(KC_DELETE); break;
+        case TD_SINGLE_TAP:
+                             if (host_keyboard_led_state().caps_lock)  
+                                 register_code(KC_CAPS_LOCK);
+                             break;
+        default: break;
+    }
+}
+
+void lshift_reset(tap_dance_state_t *state, void *user_data) {
+    switch (lshift_tap_state.state) {
+        case TD_SINGLE_HOLD: unregister_code(KC_LEFT_SHIFT); break;
+        case TD_DOUBLE_TAP:  
+        case TD_DOUBLE_HOLD: 
+                             unregister_code(KC_DELETE); break;
+        case TD_SINGLE_TAP:
+                             if (host_keyboard_led_state().caps_lock) 
+                                 unregister_code(KC_CAPS_LOCK);
+                             break;
+        default: break;
+    }
+    lshift_tap_state.state = TD_NONE;
+}
+
+// Definition for the key E
+// This key can transform into LEFT CONTROL when hold, but can also transform into È when double tapped.
+static td_tap_t e_tap_state = {
+    .is_press_action = true,
+    .state = TD_NONE
+};
+
+// Functions that control what our tap dance key does
+void e_finished(tap_dance_state_t *state, void *user_data) {
+    e_tap_state.state = cur_dance(state);
+    switch (e_tap_state.state) {
+        // Remove the layer with a single tap, this way I always have a key to remove the
+        // the layer, without knowing the previous state I had.
+        case TD_SINGLE_HOLD: register_code(KC_LEFT_CTRL); break;
+        case TD_DOUBLE_TAP: 
+                             register_code(BP_EGRV); break;
+        case TD_SINGLE_TAP:
+                             register_code(BP_E); break;
+                             break;
+        default: break;
+    }
+}
+
+void e_reset(tap_dance_state_t *state, void *user_data) {
+    switch (e_tap_state.state) {
+        case TD_SINGLE_HOLD: unregister_code(KC_LEFT_CTRL); break;
+        case TD_DOUBLE_TAP:  
+                             unregister_code(BP_EGRV); break;
+        case TD_SINGLE_TAP:
+                             unregister_code(BP_E); break;
+                             break;
+        default: break;
+    }
+    e_tap_state.state = TD_NONE;
+}
+
+
+
+//
+// Declare the tapdance table here.
+//
+tap_dance_action_t tap_dance_actions[] = {
+  [TD_C_CCED]           = ACTION_TAP_DANCE_DOUBLE(BP_C, BP_CCED),
+  [TD_PRC]              = ACTION_TAP_DANCE_DOUBLE(BP_EQL, BP_PERC),
+  [TD_W_CAPSLOCK]       = ACTION_TAP_DANCE_FN_ADVANCED(NULL, w_finished, w_reset),
+  [TD_LAYER_SYMB]       = ACTION_TAP_DANCE_FN_ADVANCED(NULL, ql_finished, ql_reset),
+  [TD_LSFT]             = ACTION_TAP_DANCE_FN_ADVANCED(NULL, lshift_finished, lshift_reset),
+  [TD_E_EE]             = ACTION_TAP_DANCE_FN_ADVANCED(NULL, e_finished, e_reset),
+};
+
diff --git a/qmk/keyboards/sofle_choc/keymaps/custom/quad_tapdance.h b/qmk/keyboards/sofle_choc/keymaps/custom/quad_tapdance.h
new file mode 100644
index 0000000..87da433
--- /dev/null
+++ b/qmk/keyboards/sofle_choc/keymaps/custom/quad_tapdance.h
@@ -0,0 +1,30 @@
+#pragma once
+
+typedef enum {
+    TD_NONE,
+    TD_UNKNOWN,
+    TD_SINGLE_TAP,
+    TD_SINGLE_HOLD,
+    TD_DOUBLE_TAP,
+    TD_DOUBLE_HOLD,
+    TD_DOUBLE_SINGLE_TAP, // Send two single taps
+    TD_TRIPLE_TAP,
+    TD_TRIPLE_HOLD
+} td_state_t;
+
+typedef struct {
+    bool is_press_action;
+    td_state_t state;
+} td_tap_t;
+
+td_state_t cur_dance(tap_dance_state_t *state);
+
+// For the x tap dance. Put it here so it can be used in any keymap
+void w_finished(tap_dance_state_t *state, void *user_data);
+void w_reset(tap_dance_state_t *state, void *user_data);
+
+void ql_finished(tap_dance_state_t *state, void *user_data);
+void ql_reset(tap_dance_state_t *state, void *user_data);
+
+void lshift_finished(tap_dance_state_t *state, void *user_data);
+void lshift_reset(tap_dance_state_t *state, void *user_data);
diff --git a/qmk/keyboards/sofle_choc/keymaps/custom/rules.mk b/qmk/keyboards/sofle_choc/keymaps/custom/rules.mk
new file mode 100644
index 0000000..5edfedd
--- /dev/null
+++ b/qmk/keyboards/sofle_choc/keymaps/custom/rules.mk
@@ -0,0 +1,6 @@
+ENCODER_MAP_ENABLE = yes
+SPLIT_KEYBOARD = yes
+TAP_DANCE_ENABLE = yes
+CAPS_WORD_ENABLE = yes
+KEY_OVERRIDE_ENABLE = yes
+SRC += quad_tapdance.c keycodes.c
-- 
cgit v1.2.3