summaryrefslogtreecommitdiffstats
path: root/quantum
diff options
context:
space:
mode:
authorprecondition <57645186+precondition@users.noreply.github.com>2023-07-07 16:18:02 +0200
committerGitHub <noreply@github.com>2023-07-08 00:18:02 +1000
commit1abf8f3e8b8a9b358be3c27867f9bee7422507f3 (patch)
tree22df0acf90ca877cf4b1066116704a310227be99 /quantum
parentbaf289112460d73e889a8a442a932bc4198ccc05 (diff)
[Feature] Send a dummy keycode to neutralize flashing modifiers in retro tap and key overrides (#20992)
Diffstat (limited to 'quantum')
-rw-r--r--quantum/action.c7
-rw-r--r--quantum/action_util.c25
-rw-r--r--quantum/action_util.h13
-rw-r--r--quantum/process_keycode/process_key_override.c9
4 files changed, 54 insertions, 0 deletions
diff --git a/quantum/action.c b/quantum/action.c
index 84ecf6da9a..6368f7398c 100644
--- a/quantum/action.c
+++ b/quantum/action.c
@@ -528,6 +528,13 @@ void process_action(keyrecord_t *record, action_t action) {
unregister_code(action.key.code);
} else {
ac_dprintf("MODS_TAP: No tap: add_mods\n");
+# if defined(RETRO_TAPPING) && defined(DUMMY_MOD_NEUTRALIZER_KEYCODE)
+ // Send a dummy keycode to neutralize flashing modifiers
+ // if the key was held and then released with no interruptions.
+ if (retro_tapping_counter == 2) {
+ neutralize_flashing_modifiers(get_mods());
+ }
+# endif
unregister_mods(mods);
}
}
diff --git a/quantum/action_util.c b/quantum/action_util.c
index 361f410d2d..909dea0595 100644
--- a/quantum/action_util.c
+++ b/quantum/action_util.c
@@ -500,3 +500,28 @@ __attribute__((weak)) void oneshot_layer_changed_kb(uint8_t layer) {
uint8_t has_anymod(void) {
return bitpop(real_mods);
}
+
+#ifdef DUMMY_MOD_NEUTRALIZER_KEYCODE
+/** \brief Send a dummy keycode in between the register and unregister event of a modifier key, to neutralize the "flashing modifiers" phenomenon.
+ *
+ * \param active_mods 8-bit packed bit-array describing the currently active modifiers (in the format GASCGASC).
+ *
+ * Certain QMK features like key overrides or retro tap must unregister a previously
+ * registered modifier before sending another keycode but this can trigger undesired
+ * keyboard shortcuts if the clean tap of a single modifier key is bound to an action
+ * on the host OS, as is for example the case for the left GUI key on Windows, which
+ * opens the Start Menu when tapped.
+ */
+void neutralize_flashing_modifiers(uint8_t active_mods) {
+ // In most scenarios, the flashing modifiers phenomenon is a problem
+ // only for a subset of modifier masks.
+ const static uint8_t mods_to_neutralize[] = MODS_TO_NEUTRALIZE;
+ const static uint8_t n_mods = ARRAY_SIZE(mods_to_neutralize);
+ for (uint8_t i = 0; i < n_mods; ++i) {
+ if (active_mods == mods_to_neutralize[i]) {
+ tap_code(DUMMY_MOD_NEUTRALIZER_KEYCODE);
+ break;
+ }
+ }
+}
+#endif
diff --git a/quantum/action_util.h b/quantum/action_util.h
index 02f6e9e6df..831caf3c0a 100644
--- a/quantum/action_util.h
+++ b/quantum/action_util.h
@@ -102,6 +102,19 @@ void use_oneshot_swaphands(void);
void clear_oneshot_swaphands(void);
#endif
+#ifdef DUMMY_MOD_NEUTRALIZER_KEYCODE
+// KC_A is used as the lowerbound instead of QK_BASIC because the range QK_BASIC...KC_A includes
+// internal keycodes like KC_NO and KC_TRANSPARENT which are unsuitable for use with `tap_code(kc)`.
+# if !(KC_A <= DUMMY_MOD_NEUTRALIZER_KEYCODE && DUMMY_MOD_NEUTRALIZER_KEYCODE <= QK_BASIC_MAX)
+# error "DUMMY_MOD_NEUTRALIZER_KEYCODE must be a basic, unmodified, HID keycode!"
+# endif
+void neutralize_flashing_modifiers(uint8_t active_mods);
+#endif
+#ifndef MODS_TO_NEUTRALIZE
+# define MODS_TO_NEUTRALIZE \
+ { MOD_BIT(KC_LEFT_ALT), MOD_BIT(KC_LEFT_GUI) }
+#endif
+
#ifdef __cplusplus
}
#endif
diff --git a/quantum/process_keycode/process_key_override.c b/quantum/process_keycode/process_key_override.c
index 17e490e67a..de628d3fec 100644
--- a/quantum/process_keycode/process_key_override.c
+++ b/quantum/process_keycode/process_key_override.c
@@ -322,6 +322,15 @@ static bool try_activating_override(const uint16_t keycode, const uint8_t layer,
clear_active_override(false);
+#ifdef DUMMY_MOD_NEUTRALIZER_KEYCODE
+ // Send a dummy keycode before unregistering the modifier(s)
+ // so that suppressing the modifier(s) doesn't falsely get interpreted
+ // by the host OS as a tap of a modifier key.
+ // For example, unintended activations of the start menu on Windows when
+ // using a GUI+<kc> key override with suppressed mods.
+ neutralize_flashing_modifiers(active_mods);
+#endif
+
active_override = override;
active_override_trigger_is_down = true;