# 重映射按键 (键盘, 鼠标和控制器)

## 目录

- 简介
- 重映射键盘和鼠标
- 备注
- 通过键盘移动鼠标光标
- 使用注册表的 "扫描码映射" 功能进行重映射
- 相关话题

## 简介

限制: 下面描述的 AutoHotkey 重映射功能通常不如直接通过 Windows 注册表进行映射那么纯粹和有效. 对于每种方法的优点和缺点, 请参阅注册表重映射..

## 重映射键盘和鼠标

内置重映射功能的语法为 `OriginKey::DestinationKey`. 例如, 只包含下面这行内容的脚本会将 `A` 重映射成 `B`:

    a::b

上面的例子并不改变 `B` 键本身. `B` 将继续发送 "b" 的键击, 除非把它重映射到其他内容, 如下面的示例所示:

    a::b
    b::a

上述例子中使用小写字母, 在大多数情况下建议这么做, 因为它也会映射相应的大写字母(即在 `CapsLock` "打开" 或 `Shift` 被按住时会发送大写形式). 与之相比, 在右边使用大写字母会强制发送大写形式. 例如, 下面的代码行在您输入 "a" 或 "A" 时都会产生大写字母 B(只要 `CapsLock` 是关闭的):

    a::B

相反, 在发送按键时, 将自动释放包含在左侧但不包含在右侧的任何修饰符. 例如, 以下两行会产生一个小写字母 "b", 当你按下 Shift+A 或 Ctrl+A 时:

    A::b
    ^a::b

### 鼠标重映射

要重映射鼠标而不是键盘, 请使用相同的方法. 例如:

示例 | 描述
--- | ---
`MButton::Shift` | 把鼠标中键映射成 `Shift`.
`XButton1::LButton` | 把第四个鼠标按钮映射成鼠标左键.
`RAlt::RButton` | 把右 `Alt` 映射成鼠标右键.

### 其他有用的映射

示例 | 描述
--- | ---
`CapsLock::Ctrl` | 把 `CapsLock` 映射成 `Ctrl` 键. 要保留打开及关闭 `CapsLock` 的功能, 需先包含重映射 `+CapsLock::CapsLock`. 这样, 当按住 `Shift` 并按下 `CapsLock` 时, 可切换 `CapsLock` 的开关状态. 因为两个重映射均允许附加修饰键被按下, 额外指定的 `+CapsLock::CapsLock` 重映射必须优先放置以便能正常工作.
`XButton2::^LButton` | 把第五个鼠标按钮(XButton2) 映射成 control-click.
`RAlt::AppsKey` | 把右 `Alt` 映射成 `Menu`(这是打开上下文菜单的按键).
`RCtrl::RWin` | 把右 `Ctrl` 映射成右 `Win`.
`Ctrl::Alt` | 把左右 `Ctrl` 映射成 `Alt`. 但是, 请参阅 alt-tab 的问题.
`^x::^c` | 把 `Ctrl`+`X` 映射成 `Ctrl`+`C`. 它也会让 `Ctrl`+`Alt`+`X` 映射成 `Ctrl`+`Alt`+`C`, 等等.
`RWin::Return` | 通过简单的return, 来禁用右 `Win`.

您可以通过将这些示例复制到一个新的文本文件(如 "Remap") 中来尝试其中任何一个示例, 然后启动文件.

有关键和鼠标按钮名称的完整列表, 请参阅按键列表.

## 备注

#HotIf 指令可用于仅在指定的窗口(或者当满足任何给定条件时) 激活选定的重映射. 例如:

    #HotIf WinActive("ahk_class Notepad")
    a::b  ; 使 'a' 键发送 'b' 键, 但仅在记事本中有效.
    #HotIf  ; 这里让后续的重映射和热键对所有窗口生效.

重映射按键或按钮在以下方面是 "完全的":

- 按住修饰符(例如 `Ctrl` 或 `Shift`) 时按下原始键, 会让那个修饰符对目标键生效. 例如, `b::a` 会在您按下 `Ctrl`+`B` 时产生 `Ctrl`+`A` 的效果.
- `CapsLock` 通常会像普通键那样影响重映射的按键.
- 按住目标键或按钮的时间和您持续按住原始键的时间一样长. 但是, 一些游戏不支持重映射; 此时, 键盘和鼠标的重映射没有效果.
- 按住被重映射的按键时, 它们的目标键会自动重复(除非是把按键重映射为鼠标按钮).

尽管被重映射的按键可以触发普通热键, 但默认情况下它不能触发鼠标热键或钩子热键(请使用 ListHotkeys 来找出哪些热键使用了 "钩子"). 例如, 如果重映射 `a::b` 有效, 那么按下 `Ctrl`+`Alt`+`A` 会触发 `^!b` 热键, 但仅在 `^!b` 不是钩子热键的时候. 如果 `^!b` 是钩子热键, 而且您希望使用 `Ctrl`+`Alt`+`A` 来执行和 `Ctrl`+`Alt`+`B` 相同的动作, 那么您可以把 `^!a` 定义为热键. 例如:

    a::b
    ^!a::
    ^!b::ToolTip "You pressed " A_ThisHotkey

或者, 使用 #InputLevel 来覆盖此默认行为. 例如:

    #InputLevel 1
    a::b

    #InputLevel 0
    ^!b::ToolTip "You pressed " A_ThisHotkey

如果在脚本启动时使用了 SendMode, 那么它会影响所有的重映射. 但是, 由于重映射使用 Send "{Blind}" 而 SendPlay 模式不完全支持 {Blind}, 所以一些映射在 SendPlay 模式中可能无法正常运行(尤其是 `Ctrl`, `Shift`, `Alt` 和 `Win`). 要解决此问题, 当您脚本中含有重映射时请避免在脚本启动时使用 `SendMode "Play"`; 而仅在整个脚本需要的地方使用 SendPlay 函数而不是 Send. 或者, 您可以把您的重映射转换为热键(如下所述), 这样就显式调用 SendEvent 而不是 Send.

如果 DestinationKey 是按键 `{`, 它必须进行转义, 例如, `x::`{`. 否则它将被解释为热键函数的左大括号.

加载脚本时, 每个重映射会被转换为一对热键. 例如, 包含重映射 `a::b` 的脚本实际包含下面的两个热键:

    *a::
    {
        SetKeyDelay -1   ; 如果目标键是鼠标按钮, 则使用 SetMouseDelay 代替.
        Send "{Blind}{b DownR}"  ; DownR 和 Down 相似, 不过使用 DownTemp 时脚本中后面的其他 Send 命令则不会假定 "b" 仍然处于按下的状态.
    }

    *a up::
    {
        SetKeyDelay -1  ; 请参阅下面的注意事项来了解在这段代码中两处 SetKeyDelay 都没有指定按键时长的原因.
        Send "{Blind}{b Up}"
    }

但是, 上面的两个热键在下列环境中会发生变化:

1. 当源按键为左 `Ctrl` 而目标键为 `Alt`, 会把 `Send "{Blind}{LAlt DownR}"` 这行语句替换为 `Send "{Blind}{LCtrl up}{LAlt DownR}"`. 如果原按键为右 `Ctrl` 也会如此, 除非使用了 `{RCtrl up}`.
2. 当键盘按键被重映射为鼠标按钮时(例如 `RCtrl::RButton`), 在上面的热键中会使用 SetMouseDelay 代替 SetKeyDelay. 而且, 上面的首个热键会被替换为下面的热键, 这样避免了由于键盘的自动重复功能生成的重复的鼠标点击:

       *RCtrl::
       {
           SetMouseDelay -1
           if not GetKeyState("RButton")  ; 即鼠标右键还没有按下.
               Send "{Blind}{RButton DownR}"
       }

3. 当源按键为自定义组合键时, 通配符修饰符(*) 被省略以允许热键工作.

注意, SetKeyDelay 的第二个参数(按下持续时间) 在上面的热键中被省略了. 这是因为按下持续时间不适用于独立的按键按下或按键弹起事件, 例如 `{b down}` 和 `{b up}`. 但是, 它适用于修饰键(Shift, Ctrl, Alt 和 Win) 状态的改变, 这会影响像 `a::B` 或 `a::^b` 这样的重映射. 因此, 脚本在脚本启动期间设置的任何按下持续时间将会应用于所有此类重映射.

由于重映射会被简单转换为上面描述的热键, 所以 Suspend 也会影响它们. 同样地, Hotkey 函数可以禁用或修改重映射. 例如, 下面的两个函数会禁用重映射 `a::b`.

    Hotkey "*a", "Off"
    Hotkey "*a up", "Off"

Alt-tab 的问题: 如果您把一个按键或鼠标按钮映射为 `Alt`, 那么这个键很可能无法正确实现 alt-tab 的功能. 一个可能的解决方法是添加热键 `*Tab::Send "{Blind}{Tab}"` -- 但是需要注意这样很可能妨碍使用真正的 `Alt` 实现 alt-tab 的功能. 因此, 只有当您仅通过重映射的按键和/或 alt-tab 热键实现 alt-tab 功能时, 才应使用它.

除了按键列表 页面中的按键和鼠标按钮外, 源按键还可以是特殊按键页面描述的虚拟键(VKnn) 或扫描码(SCnnn). 对于目标键同样如此, 不过它还可以在虚拟键后指定扫描码. 例如, 在大多数键盘布局中 `sc01e::vk42sc030` 等同于 `a::b`.

要禁用按键而不进行重映射, 请把它设置为只含有 returns 的热键. 例如, `F1::return` 会禁用 `F1`.

内置的重映射方法不支持下列按键:

- 鼠标滚轮(WheelUp/Down/Left/Right).
- "Pause" 不能作为目标键名(因为它匹配内置函数的名称). 请使用 `vk13` 或对应的扫描码代替.
- 大括号 {} 不能作为目标键. 作为替代请使用 VK/SC 方法; 例如 `x::+sc01A` 和 `y::+sc01B`.

## 通过键盘移动鼠标光标

键盘可以用来移动鼠标光标, 如功能齐全的键盘到鼠标脚本所示. 由于该脚本提供了鼠标平滑移动, 加速和其他特性, 所以如果您想使用键盘进行大量的鼠标操作, 那么推荐使用该脚本. 相比之下, 下面的例子是一个更简单的例子:

    *#up::MouseMove 0, -10, 0, "R"  ; Win+UpArrow 热键 => 上移光标
    *#Down::MouseMove 0, 10, 0, "R"  ; Win+DownArrow => 下移光标
    *#Left::MouseMove -10, 0, 0, "R"  ; Win+LeftArrow => 左移光标
    *#Right::MouseMove 10, 0, 0, "R"  ; Win+RightArrow => 右移光标

    *<#RCtrl::  ; 左 Win + 右 Control => Left-click(按住 Control/Shift 来进行 Control-Click 或 Shift-Click).
    {
        SendEvent "{Blind}{LButton down}"
        KeyWait "RCtrl"  ; 防止键盘自动重复导致的重复鼠标点击.
        SendEvent "{Blind}{LButton up}"
    }

    *<#AppsKey::  ; 左 Win + AppsKey => Right-click
    {
        SendEvent "{Blind}{RButton down}"
        KeyWait "AppsKey"  ; 防止键盘自动重复导致重复的鼠标点击.
        SendEvent "{Blind}{RButton up}"
    }

## 使用注册表的 "扫描码映射" 功能进行重映射

优点:

- 注册表的重映射功能通常比 AutoHotkey 的重映射更加直接有效. 例如, 它能适应于更大范围的游戏, 它不存在 alt-tab 的问题, 并且它能触发 AutoHotkey 的钩子热键(然而 AutoHotkey 的重映射需要间接的方法才行).
- 如果您会手动修改注册表条目(下面进行阐述), 那么不需要任何额外的软件就可以重映射您的键盘. 即使您使用 KeyTweak 来修改注册表条目, 那么完成后 KeyTweak 不需要一直保持运行(而 AutoHotkey 需要一直运行, 重映射才会生效).

缺点:

- 注册表重映射比较不灵活: 每次修改映射都必须重启后才会生效.
- 效果是全局的: 它不能创建针对特定用户, 应用程序或环境的重映射.
- 它无法发送含修饰键 `Shift`, `Ctrl`, `Alt` 或 `AltGr` 的组合键. 例如, 它不能把小写字符映射为大写字符.
- 它只支持键盘(AutoHotkey 还支持鼠标重映射和一些受限的控制器重映射).

如何将更改应用于注册表: 使用注册表重映射按键至少有两种方法:

1. 使用像 KeyTweak(免费软件) 这样的程序可视化重映射按键. 它会自动修改注册表.
2. 通过手动创建的 .reg 文件(纯文本) 重映射按键并把其导入注册表. 演示的例子, 请参阅 www.autohotkey.com/forum/post-56216.html#56216

## 相关话题

- 按键, 鼠标按钮控制器控制列表
- GetKeyState
- 重映射控制器

dftg