From 81d2e994cce82951527c086234ac859b40bd87df Mon Sep 17 00:00:00 2001
From: Hadeed Ahmad <hadeedji@gmail.com>
Date: Mon, 11 Dec 2023 02:57:27 +0500
Subject: [PATCH] WIP: Start improving the keymap

---
 keyboards/crkbd/keymaps/hadeed/keymap.c  | 88 +++++++++++++++++-------
 keyboards/crkbd/keymaps/hadeed/oneshot.c | 59 ++++++++++++++++
 keyboards/crkbd/keymaps/hadeed/oneshot.h | 33 +++++++++
 keyboards/crkbd/keymaps/hadeed/rules.mk  |  4 ++
 4 files changed, 161 insertions(+), 23 deletions(-)
 create mode 100644 keyboards/crkbd/keymaps/hadeed/oneshot.c
 create mode 100644 keyboards/crkbd/keymaps/hadeed/oneshot.h

diff --git a/keyboards/crkbd/keymaps/hadeed/keymap.c b/keyboards/crkbd/keymaps/hadeed/keymap.c
index 0f6e99f..b24201e 100644
--- a/keyboards/crkbd/keymaps/hadeed/keymap.c
+++ b/keyboards/crkbd/keymaps/hadeed/keymap.c
@@ -1,21 +1,22 @@
 #include QMK_KEYBOARD_H
 
+#include "oneshot.h"
+
 enum LAYERS {
     BASE,
-    NUM,
-    SYM,
+    NAV,
 };
 
-#define HRM_A LALT_T(KC_A)
-#define HRM_R LGUI_T(KC_R)
-#define HRM_S LCTL_T(KC_S)
-#define HRM_T LSFT_T(KC_T)
-#define HRM_N RSFT_T(KC_N)
-#define HRM_E RCTL_T(KC_E)
-#define HRM_I RGUI_T(KC_I)
-#define HRM_O LALT_T(KC_O)
+enum KEYCODES {
+    LC_NAV = MO(NAV),
+    LC_NUM = MO(NUM),
+    LC_SYM = MO(SYM),
 
-#define _KC_TAB LT(NUM, KC_TAB)
+    OS_SHFT = SAFE_RANGE,
+    OS_CTRL,
+    OS_GUI,
+    OS_ALT
+};
 
 const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
     /*
@@ -28,23 +29,64 @@ const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
     */
 
     [BASE] = LAYOUT_split_3x5_3(
-        KC_Q,    KC_W,    KC_F,    KC_P,    KC_B,                               KC_J,    KC_L,    KC_U,    KC_Y,    KC_SCLN,
-        HRM_A,   HRM_R,   HRM_S,   HRM_T,   KC_G,                               KC_M,    HRM_N,   HRM_E,   HRM_I,   HRM_O,
+        KC_Q,    KC_W,    KC_F,    KC_P,    KC_B,                               KC_J,    KC_L,    KC_U,    KC_Y,    KC_QUOT,
+        KC_A,    KC_R,    KC_S,    KC_T,    KC_G,                               KC_M,    KC_N,    KC_E,    KC_I,    KC_O,
         KC_Z,    KC_X,    KC_C,    KC_D,    KC_V,                               KC_K,    KC_H,    KC_COMM, KC_DOT,  KC_SLSH,
-                                   _KC_TAB, KC_ENT, KC_ESC,            KC_BSPC, KC_SPC,  MO(SYM)
+                                   QK_REP,  LC_NAV, KC_ENT,            KC_SPC,  LC_NUM,  LC_SYM
     ),
 
     [NUM] = LAYOUT_split_3x5_3(
-        _______, _______, _______, _______, _______,                            _______, _______, _______, _______, _______,
-        KC_9,    KC_5,    KC_0,    KC_3,    KC_7,                               KC_6,    KC_2,    KC_1,     KC_4,    KC_8,
-        _______, _______, _______, _______, _______,                            _______, _______, _______, _______, _______,
-                                   _______, _______, _______,          _______, _______, _______
+        _______, _______, _______, _______, _______,                            _______, KC_7,    KC_8,    KC_9, _______,
+        OS_ALT,  OS_GUI,  OS_CTRL, OS_SHFT, CW_TOGG,                            _______, KC_4,    KC_5,    KC_6, _______,
+        _______, _______, _______, _______, _______,                            _______, KC_1,    KC_2,    KC_3, _______,
+                                   _______, _______, _______,          _______, KC_0,    _______
     ),
 
-    [SYM] = LAYOUT_split_3x5_3(
-        KC_GRV,  KC_DQUO, KC_LBRC, KC_RBRC, KC_AT,                              KC_PERC, KC_HASH, KC_ASTR, KC_SLSH, KC_COLN,
-        KC_BSLS, KC_QUOT, KC_LPRN, KC_RPRN, KC_AMPR,                            KC_PIPE, KC_EQL,  KC_MINS, KC_PLUS, KC_EXLM,
-        QK_BOOT, KC_TILD, KC_LCBR, KC_RCBR, KC_CIRC,                            KC_DLR,  KC_UNDS, KC_LABK, KC_RABK, KC_QUES,
-                                   _______, _______, _______,          _______, _______, _______
+    [NAV] = LAYOUT_split_3x5_3(
+        _______, _______, _______, _______, _______,                            _______, _______, _______, _______, _______,
+        OS_ALT,  OS_GUI,  OS_CTRL, OS_SHFT, CW_TOGG,                            KC_LEFT, KC_DOWN, KC_UP,   KC_RGHT, KC_BSPC,
+        _______, _______, _______, _______, _______,                            KC_HOME, KC_PGDN, KC_PGUP, KC_END,  KC_DEL,
+                                   QK_BOOT, _______, _______,          KC_TAB,  KC_ESC,  _______
     ),
 };
+
+bool is_oneshot_cancel_key(uint16_t keycode) {
+    switch (keycode) {
+        case LC_NAV:
+        case LC_NUM:
+        case LC_SYM:
+            return true;
+    }
+
+    return false;
+}
+
+bool is_oneshot_ignored_key(uint16_t keycode) {
+    switch (keycode) {
+        case LC_NAV:
+        case LC_NUM:
+        case LC_SYM:
+        case OS_SHFT:
+        case OS_CTRL:
+        case OS_ALT:
+        case OS_GUI:
+            return true;
+    }
+
+    return false;
+}
+
+static oneshot_state os_shft_state = os_up_unqueued;
+static oneshot_state os_ctrl_state = os_up_unqueued;
+static oneshot_state os_alt_state = os_up_unqueued;
+static oneshot_state os_cmd_state = os_up_unqueued;
+
+bool process_record_user(uint16_t keycode, keyrecord_t *record) {
+    update_oneshot(&os_shft_state, KC_LSFT, OS_SHFT, keycode, record);
+    update_oneshot(&os_ctrl_state, KC_LCTL, OS_CTRL, keycode, record);
+    update_oneshot(&os_alt_state, KC_LALT, OS_ALT, keycode, record);
+    update_oneshot(&os_cmd_state, KC_LCMD, OS_GUI, keycode, record);
+
+    return true;
+}
+
diff --git a/keyboards/crkbd/keymaps/hadeed/oneshot.c b/keyboards/crkbd/keymaps/hadeed/oneshot.c
new file mode 100644
index 0000000..3d69c7c
--- /dev/null
+++ b/keyboards/crkbd/keymaps/hadeed/oneshot.c
@@ -0,0 +1,59 @@
+// https://github.com/callum-oakley/qmk_firmware/tree/master/users/callum
+
+#include "oneshot.h"
+
+void update_oneshot(
+    oneshot_state *state,
+    uint16_t mod,
+    uint16_t trigger,
+    uint16_t keycode,
+    keyrecord_t *record
+) {
+    if (keycode == trigger) {
+        if (record->event.pressed) {
+            // Trigger keydown
+            if (*state == os_up_unqueued) {
+                register_code(mod);
+            }
+            *state = os_down_unused;
+        } else {
+            // Trigger keyup
+            switch (*state) {
+            case os_down_unused:
+                // If we didn't use the mod while trigger was held, queue it.
+                *state = os_up_queued;
+                break;
+            case os_down_used:
+                // If we did use the mod while trigger was held, unregister it.
+                *state = os_up_unqueued;
+                unregister_code(mod);
+                break;
+            default:
+                break;
+            }
+        }
+    } else {
+        if (record->event.pressed) {
+            if (is_oneshot_cancel_key(keycode) && *state != os_up_unqueued) {
+                // Cancel oneshot on designated cancel keydown.
+                *state = os_up_unqueued;
+                unregister_code(mod);
+            }
+        } else {
+            if (!is_oneshot_ignored_key(keycode)) {
+                // On non-ignored keyup, consider the oneshot used.
+                switch (*state) {
+                case os_down_unused:
+                    *state = os_down_used;
+                    break;
+                case os_up_queued:
+                    *state = os_up_unqueued;
+                    unregister_code(mod);
+                    break;
+                default:
+                    break;
+                }
+            }
+        }
+    }
+}
diff --git a/keyboards/crkbd/keymaps/hadeed/oneshot.h b/keyboards/crkbd/keymaps/hadeed/oneshot.h
new file mode 100644
index 0000000..ce5ddf7
--- /dev/null
+++ b/keyboards/crkbd/keymaps/hadeed/oneshot.h
@@ -0,0 +1,33 @@
+// https://github.com/callum-oakley/qmk_firmware/tree/master/users/callum
+
+#pragma once
+
+#include QMK_KEYBOARD_H
+
+// Represents the four states a oneshot key can be in
+typedef enum {
+    os_up_unqueued,
+    os_up_queued,
+    os_down_unused,
+    os_down_used,
+} oneshot_state;
+
+// Custom oneshot mod implementation that doesn't rely on timers. If a mod is
+// used while it is held it will be unregistered on keyup as normal, otherwise
+// it will be queued and only released after the next non-mod keyup.
+void update_oneshot(
+    oneshot_state *state,
+    uint16_t mod,
+    uint16_t trigger,
+    uint16_t keycode,
+    keyrecord_t *record
+);
+
+// To be implemented by the consumer. Defines keys to cancel oneshot mods.
+bool is_oneshot_cancel_key(uint16_t keycode);
+
+// To be implemented by the consumer. Defines keys to ignore when determining
+// whether a oneshot mod has been used. Setting this to modifiers and layer
+// change keys allows stacking multiple oneshot modifiers, and carrying them
+// between layers.
+bool is_oneshot_ignored_key(uint16_t keycode);
diff --git a/keyboards/crkbd/keymaps/hadeed/rules.mk b/keyboards/crkbd/keymaps/hadeed/rules.mk
index dd0c8b4..469ec69 100644
--- a/keyboards/crkbd/keymaps/hadeed/rules.mk
+++ b/keyboards/crkbd/keymaps/hadeed/rules.mk
@@ -1 +1,5 @@
+SRC += oneshot.c
 CONVERT_TO=kb2040
+
+CAPS_WORD_ENABLE = yes
+REPEAT_KEY_ENABLE = yes