QMK 固件 US ANSI Shifted 符号键码:KC_TILDE 等 Shift 符号快捷键的实现原理与使用限制

📅 发布时间:2026/9/14 6:01:47
QMK 固件 US ANSI Shifted 符号键码:KC_TILDE 等 Shift 符号快捷键的实现原理与使用限制
QMK 固件 US ANSI Shifted 符号键码KC_TILDE 等 Shift 符号快捷键的实现原理与使用限制【免费下载链接】qmk_firmwareOpen-source keyboard firmware for Atmel AVR and Arm USB families项目地址: https://gitcode.com/GitHub_Trending/qm/qmk_firmwareQMK 固件为 US ANSI 布局提供了一组 Shifted 符号 键码如KC_TILDE、KC_EXCLAIM、KC_AT它们本身不是独立的 HID 按键而是LSFT(kc)的语法糖——按下时自动同时发送左 Shift 与对应的基础键码让键帽上的符号可以直接写入键位定义。本文基于仓库中的 US ANSI Shifted 符号键码文档 与 keymap_us.h 源码完整梳理这 22 个键码及其别名、宏展开机制、底层注册流程以及 Mod-Tap/Layer-Tap 中不可用、Windows 远程桌面丢字符这两个重要限制的原因与解决办法。一、定位这些键码不是真正的键码文档首先给出的关键结论是这些键码对应的是标准 US ANSI 键盘上需要 shift 才能打出的字符。它们没有自己的 HID 键码US ANSI 布局中~!#$%^*()_{}|:?在底层就是基础键加 ShiftQMK 只是为它们提供了更直观的宏名作为LSFT(kc)的快捷方式。因此使用KC_EXCLAIM时固件实际发送的是左 Shift 1这一组合而不是某个独立的 ! 键码。这个语义差异正是后文所有限制Mod-Tap 中失效、远程桌面丢字的根源。在源码层面这组键码统一定义在 keymap_us.h 中全部展开为S(...)宏// quantum/keymap_extras/keymap_us.h节选 #define KC_TILD S(KC_GRAVE) // ~ #define KC_EXLM S(KC_1) // ! #define KC_AT S(KC_2) // #define KC_HASH S(KC_3) // # #define KC_DLR S(KC_4) // $ #define KC_PERC S(KC_5) // % #define KC_CIRC S(KC_6) // ^ #define KC_AMPR S(KC_7) // #define KC_ASTR S(KC_8) // * #define KC_LPRN S(KC_9) // ( #define KC_RPRN S(KC_0) // ) #define KC_UNDS S(KC_MINUS) // _ #define KC_PLUS S(KC_EQUAL) // #define KC_LCBR S(KC_LEFT_BRACKET) // { #define KC_RCBR S(KC_RIGHT_BRACKET) // } #define KC_PIPE S(KC_BACKSLASH) // | #define KC_COLN S(KC_SEMICOLON) // : #define KC_DQUO S(KC_QUOTE) // #define KC_LABK S(KC_COMMA) // #define KC_RABK S(KC_DOT) // #define KC_QUES S(KC_SLASH) // ?二、完整键码与别名对照表以下是 原文档 提供的完整键码表。QMK 同时提供两套命名风格短别名历史遗留如KC_TILD、KC_EXLM和全拼名如KC_TILDE、KC_EXCLAIM。两者在 keymap_us.h 中通过#define相互绑定例如#define KC_TILDE KC_TILD因此在 keymap 中混用完全等价。键码全拼名别名对应符号等价于KC_TILDEKC_TILD~S(KC_GRAVE)KC_EXCLAIMKC_EXLM!S(KC_1)KC_AT—S(KC_2)KC_HASH—#S(KC_3)KC_DOLLARKC_DLR$S(KC_4)KC_PERCENTKC_PERC%S(KC_5)KC_CIRCUMFLEXKC_CIRC^S(KC_6)KC_AMPERSANDKC_AMPRS(KC_7)KC_ASTERISKKC_ASTR*S(KC_8)KC_LEFT_PARENKC_LPRN(S(KC_9)KC_RIGHT_PARENKC_RPRN)S(KC_0)KC_UNDERSCOREKC_UNDS_S(KC_MINUS)KC_PLUS—S(KC_EQUAL)KC_LEFT_CURLY_BRACEKC_LCBR{S(KC_LEFT_BRACKET)KC_RIGHT_CURLY_BRACEKC_RCBR}S(KC_RIGHT_BRACKET)KC_PIPE—\|S(KC_BACKSLASH)KC_COLONKC_COLN:S(KC_SEMICOLON)KC_DOUBLE_QUOTEKC_DQUO、KC_DQTS(KC_QUOTE)KC_LEFT_ANGLE_BRACKETKC_LABK、KC_LTS(KC_COMMA)KC_RIGHT_ANGLE_BRACKETKC_RABK、KC_GTS(KC_DOT)KC_QUESTIONKC_QUES?S(KC_SLASH)其中KC_DOUBLE_QUOTE、KC_LEFT_ANGLE_BRACKET、KC_RIGHT_ANGLE_BRACKET各有两个别名例如KC_DQT与KC_DQUO都指向同一个宏见 keymap_us.h 中的#define KC_DQT KC_DQUO、#define KC_LT KC_LABK、#define KC_GT KC_RABK写 keymap 时选哪种拼法不影响行为。三、宏展开链路从 KC_AT 到发送 Left Shift 2理解这些键码的关键在于S()宏的定义链。在 quantum_keycodes.h 中#define S(kc) LSFT(kc)而LSFT在同文件 第 46 行 定义#define LSFT(kc) (QK_LSFT | (kc))因此KC_AT在编译期就静态展开为QK_LSFT | KC_2这样的 16 位值高 8 位携带 左 Shift 标志低 8 位保留基础键码KC_2注意QK_LSFT的值落在低 8 位键码区的高位上宏直接按位或即可编码出组合语义。这与KC_LSPO/KC_RSPC这类真正独立的 HID 键码完全不同——后者不需要 Shift 参与。运行时的注册流程按键被判定为按下后会进入 quantum.c 中的 16 位键码注册入口__attribute__((weak)) void register_code16(uint16_t code) { if (IS_MODIFIER_KEYCODE(code) || code KC_NO) { do_code16(code, register_mods); } else { do_code16(code, register_weak_mods); } register_code(code); }其中do_code16调用extract_mod_bitsquantum.c 第 102 行解析出要发送的修饰键当code QK_LSFT成立且不属于右修饰键区间时执行if (code QK_LSFT) mods_to_send | MOD_BIT(KC_LEFT_SHIFT);随后才调用register_code(code)发送剥掉标志位后的基础键码。也就是说KC_HASH按下时固件实际产生两个动作先弱注册左 Shift 修饰键再注册KC_3松键时逆序释放——这就是发送 Left Shift 与未 Shift 的键码而非符号本身的底层机制。四、限制一不能用于 Mod-Tap / Layer-Tap原文档 的 Caveats 部分指出这些键码不能用在 Mod-Tap 或 Layer-Tap 中因为键码里携带的修饰键会被忽略。从源码结构看这一限制可以直接验证。MT()与LT()宏在 quantum_keycodes.h 中定义#define LT(layer, kc) (QK_LAYER_TAP | (((layer) 0xF) 8) | ((kc) 0xFF)) #define MT(mod, kc) (QK_MOD_TAP | (((mod) 0x1F) 8) | ((kc) 0xFF))两个宏都只对kc做了 0xFF掩码。而KC_EXCLAIM展开后的QK_LSFT | KC_1中QK_LSFT的标志位正好位于低 8 位内被掩码掉的位置组合语义随之丢失——tap 分支最终只会触发KC_1本身而不是!。正确写法是在 Mod-Tap/Layer-Tap 中显式传入基础键或者把整个组合交给外层宏例如// 错误KC_EXCLAIM 的 Shift 标志被 MT 掩码丢弃 MT(MOD_LSFT, KC_EXCLAIM) // 正确一tap 部分直接用基础键码 MT(MOD_LSFT, KC_1) // 正确二需要符号输出时用 LT/MT 之外的方式组合或在 tap 分支 // 里显式使用 S(kc) 展开后的完整键值具体取决于想要的行为同理任何在 keymap 宏内嵌套组合宏的场景都应先确认内层键值是否会被外层宏的位运算破坏这是 QMK 键码位编码模型的通用心智负担。五、限制二Windows 远程桌面丢字符及修复方法原文档 还记录了第二个坑在 Windows 上使用 Remote Desktop Connection 时可能打不出这些符号。原因是这些键码触发的 Shift 按下与释放极快弱修饰键在发送基础键码前后几乎瞬时完成远程桌面的输入转发链路可能漏掉这对事件。文档给出的官方修复步骤是打开 Remote Desktop Connection点击Show Options进入Local Resources本地资源选项卡在 Keyboard 区域把下拉框从默认值改为On this Computer。这样键盘事件在本地处理后再转发字符即可正常输出。需要说明的适用前提该问题只出现在本地 QMK 键盘 → 远程桌面 → 远程会话这一特定链路且与 Shifted 符号键码发送速度极快的特性直接相关换成LCTL/LSFT长按组合或KC_LBRC等真实键码则不受影响。六、使用建议小结静态按键位普通 keymap 层、KC_TILDE直接填进数组是最常用且完全安全的场景等价于手写S(KC_GRAVE)但可读性更好动态场景Mod-Tap、Layer-Tap、自定义on_press回调中避免直接传入这些 Shifted 键码防止标志位被外层宏掩码丢弃跨平台注意它们只编码 US ANSI 的符号位置KC_LABKS(KC_COMMA)等在 US ISO 或其他布局下对应位置的实际字符会不同——这是布局特性不是键码缺陷远程桌面用户按第五节步骤调整 RDP 键盘选项即可消除丢字。七、相关文件索引文件作用docs/keycodes_us_ansi_shifted.md本文档键码表、Caveats、RDP 修复步骤quantum/keymap_extras/keymap_us.h全部 22 个 Shifted 符号键码及别名的#define定义quantum/quantum_keycodes.hLSFT/S宏定义LT/MT宏的 0xFF 掩码逻辑quantum/quantum.cextract_mod_bits与register_code16QK_LSFT标志到左 Shift 修饰键的解析与发送【免费下载链接】qmk_firmwareOpen-source keyboard firmware for Atmel AVR and Arm USB families项目地址: https://gitcode.com/GitHub_Trending/qm/qmk_firmware创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考