]> git.donarmstrong.com Git - qmk_firmware.git/blobdiff - tmk_core/common/action.c
Merge remote-tracking branch 'remotes/jackhumbert/master'
[qmk_firmware.git] / tmk_core / common / action.c
index 2ccc0e0b947bdd79342e3b31e02b89a02de72397..be6dea2b799d6a8223984b52c86095b2caf46fae 100644 (file)
@@ -46,28 +46,42 @@ void action_exec(keyevent_t event)
 #ifndef NO_ACTION_TAPPING
     action_tapping_process(record);
 #else
-    process_action(&record);
+    process_record(&record);
     if (!IS_NOEVENT(record.event)) {
         dprint("processed: "); debug_record(record); dprintln();
     }
 #endif
 }
 
-__attribute__ ((weak))
-void process_action_kb(keyrecord_t *record) {}
+#if !defined(NO_ACTION_LAYER) && defined(PREVENT_STUCK_MODIFIERS)
+bool disable_action_cache = false;
 
-void process_action(keyrecord_t *record)
+void process_record_nocache(keyrecord_t *record)
 {
-    keyevent_t event = record->event;
-#ifndef NO_ACTION_TAPPING
-    uint8_t tap_count = record->tap.count;
+    disable_action_cache = true;
+    process_record(record);
+    disable_action_cache = false;
+}
+#else
+void process_record_nocache(keyrecord_t *record)
+{
+    process_record(record);
+}
 #endif
 
-    if (IS_NOEVENT(event)) { return; }
+__attribute__ ((weak))
+bool process_record_quantum(keyrecord_t *record) {
+    return true;
+}
 
-    process_action_kb(record);
+void process_record(keyrecord_t *record) 
+{
+    if (IS_NOEVENT(record->event)) { return; }
 
-    action_t action = layer_switch_get_action(event.key);
+    if(!process_record_quantum(record))
+        return;
+
+    action_t action = store_or_get_action(record->event.pressed, record->event.key);
     dprint("ACTION: "); debug_action(action);
 #ifndef NO_ACTION_LAYER
     dprint(" layer_state: "); layer_debug();
@@ -75,10 +89,37 @@ void process_action(keyrecord_t *record)
 #endif
     dprintln();
 
+    process_action(record, action);
+}
+
+void process_action(keyrecord_t *record, action_t action)
+{
+    bool do_release_oneshot = false;
+    keyevent_t event = record->event;
+#ifndef NO_ACTION_TAPPING
+    uint8_t tap_count = record->tap.count;
+#endif
+
+#if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
+    if (has_oneshot_layer_timed_out()) {
+        dprintf("Oneshot layer: timeout\n");
+        clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED);
+    }
+#endif
+
     if (event.pressed) {
         // clear the potential weak mods left by previously pressed keys
         clear_weak_mods();
     }
+
+#ifndef NO_ACTION_ONESHOT
+    // notice we only clear the one shot layer if the pressed key is not a modifier.
+    if (is_oneshot_layer_active() && event.pressed && !IS_MOD(action.key.code)) {
+        clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED);
+        do_release_oneshot = !is_oneshot_layer_active();
+    }
+#endif
+
     switch (action.kind.id) {
         /* Key and Mods */
         case ACT_LMODS:
@@ -88,14 +129,24 @@ void process_action(keyrecord_t *record)
                                                                 action.key.mods<<4;
                 if (event.pressed) {
                     if (mods) {
-                        add_weak_mods(mods);
+                        if (IS_MOD(action.key.code)) {
+                            // e.g. LSFT(KC_LGUI): we don't want the LSFT to be weak as it would make it useless.
+                            // this also makes LSFT(KC_LGUI) behave exactly the same as LGUI(KC_LSFT)
+                            add_mods(mods);
+                        } else {
+                            add_weak_mods(mods);
+                        }
                         send_keyboard_report();
                     }
                     register_code(action.key.code);
                 } else {
                     unregister_code(action.key.code);
                     if (mods) {
-                        del_weak_mods(mods);
+                        if (IS_MOD(action.key.code)) {
+                            del_mods(mods);
+                        } else {
+                            del_weak_mods(mods);
+                        }
                         send_keyboard_report();
                     }
                 }
@@ -113,24 +164,37 @@ void process_action(keyrecord_t *record)
                         // Oneshot modifier
                         if (event.pressed) {
                             if (tap_count == 0) {
+                                dprint("MODS_TAP: Oneshot: 0\n");
                                 register_mods(mods);
-                            }
-                            else if (tap_count == 1) {
+                            } else if (tap_count == 1) {
                                 dprint("MODS_TAP: Oneshot: start\n");
                                 set_oneshot_mods(mods);
-                            }
-                            else {
+                    #if defined(ONESHOT_TAP_TOGGLE) && ONESHOT_TAP_TOGGLE > 1
+                            } else if (tap_count == ONESHOT_TAP_TOGGLE) {
+                                dprint("MODS_TAP: Toggling oneshot");
+                                clear_oneshot_mods();
+                                set_oneshot_locked_mods(mods);
+                                register_mods(mods);
+                    #endif
+                            } else {
                                 register_mods(mods);
                             }
                         } else {
                             if (tap_count == 0) {
                                 clear_oneshot_mods();
                                 unregister_mods(mods);
-                            }
-                            else if (tap_count == 1) {
+                            } else if (tap_count == 1) {
                                 // Retain Oneshot mods
-                            }
-                            else {
+                    #if defined(ONESHOT_TAP_TOGGLE) && ONESHOT_TAP_TOGGLE > 1
+                                if (mods & get_mods()) {
+                                    clear_oneshot_locked_mods();
+                                    clear_oneshot_mods();
+                                    unregister_mods(mods);
+                                }
+                            } else if (tap_count == ONESHOT_TAP_TOGGLE) {
+                                // Toggle Oneshot Layer
+                    #endif
+                            } else {
                                 clear_oneshot_mods();
                                 unregister_mods(mods);
                             }
@@ -283,6 +347,44 @@ void process_action(keyrecord_t *record)
                     event.pressed ? layer_move(action.layer_tap.val) :
                                     layer_clear();
                     break;
+            #ifndef NO_ACTION_ONESHOT
+                case OP_ONESHOT:
+                    // Oneshot modifier
+                #if defined(ONESHOT_TAP_TOGGLE) && ONESHOT_TAP_TOGGLE > 1
+                    do_release_oneshot = false;
+                    if (event.pressed) {
+                        del_mods(get_oneshot_locked_mods());
+                        if (get_oneshot_layer_state() == ONESHOT_TOGGLED) {
+                            reset_oneshot_layer();
+                            layer_off(action.layer_tap.val);
+                            break;
+                        } else if (tap_count < ONESHOT_TAP_TOGGLE) {
+                            layer_on(action.layer_tap.val);
+                            set_oneshot_layer(action.layer_tap.val, ONESHOT_START);
+                        }
+                    } else {
+                        add_mods(get_oneshot_locked_mods());
+                        if (tap_count >= ONESHOT_TAP_TOGGLE) {
+                            reset_oneshot_layer();
+                            clear_oneshot_locked_mods();
+                            set_oneshot_layer(action.layer_tap.val, ONESHOT_TOGGLED);
+                        } else {
+                            clear_oneshot_layer_state(ONESHOT_PRESSED);
+                        }
+                    }
+                #else
+                    if (event.pressed) {
+                        layer_on(action.layer_tap.val);
+                        set_oneshot_layer(action.layer_tap.val, ONESHOT_START);
+                    } else {
+                        clear_oneshot_layer_state(ONESHOT_PRESSED);
+                        if (tap_count > 1) {
+                            clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED);
+                        }
+                    }
+                #endif
+                    break;
+            #endif
                 default:
                     /* tap key */
                     if (event.pressed) {
@@ -346,6 +448,18 @@ void process_action(keyrecord_t *record)
         default:
             break;
     }
+
+#ifndef NO_ACTION_ONESHOT
+    /* Because we switch layers after a oneshot event, we need to release the
+     * key before we leave the layer or no key up event will be generated.
+     */
+    if (do_release_oneshot && !(get_oneshot_layer_state() & ONESHOT_PRESSED )   ) {
+        record->event.pressed = false;
+        layer_on(get_oneshot_layer());
+        process_record(record);
+        layer_off(get_oneshot_layer());
+    }
+#endif
 }
 
 
@@ -534,6 +648,7 @@ bool is_tap_key(keypos_t key)
             switch (action.layer_tap.code) {
                 case 0x00 ... 0xdf:
                 case OP_TAP_TOGGLE:
+                case OP_ONESHOT:
                     return true;
             }
             return false;