如何重新映射特定的按键或设备?

我正在寻找一种在Ubuntu中重新映射特定按键的方法。
例如,
我想将PgUp键改为Home,或者将PgDown键改为End
在Ubuntu/GNOME中是否存在内置命令或工具来重新分配按键?

1请查看我在这里的回答:链接。复制粘贴相同的答案并不值得。也许它会对你有所帮助。 - Rahul Virpara
2这里有没有类似于Emacs的绑定,可以使用CTRL-P/N进行单位步骤的操作? - Léo Léopold Hertz 준영
1自从一段时间以来,xmodmap已经被弃用了!要获得系统范围的设置,您必须使用xkb。因此,请编辑位于/usr/share/X11/xkb/symbols/中的语言文件以添加您的更改。请参阅http://askubuntu.com/a/898462/34298 - rubo77
如果你想重新映射键盘按键或鼠标按钮到特定的键位,可以使用sezanzeb开发的"Input Remapper"。它非常简单易用,有图形界面,并且功能稳定可靠。我刚刚设置了一个特定的快捷键来模拟键盘按键,效果很好。 - Allexj
11个回答

注意:从2013年开始,Ubuntu和其派生版本不再使用xmodmap,而是改为使用xkb。更多信息请参考this answer。下面的回答对于当前版本已经不再适用。
要重新映射某些键,您需要两个工具。第一个是xev(命令行工具),第二个是xmodmap(同样是命令行工具)。在Ubuntu中,这两个工具都应该可以直接使用,无需额外安装。
  1. 打开终端窗口并运行 xev。现在它是活动的,并等待你按下一个键。然后按下你想要改变行为的键,比如 PgUp

  2. xev 将输出一些关于按下键的信息。第三行很重要。它应该类似于:

     state 0x10, keycode 110 (keysym 0xff55, Prior), same_screen YES,
    

    在这个例子中,Prior 是当前分配给该键的行为的名称,键码是用来识别该键的内部 ID。 现在用另一个键,比如 PgDown,得到以下输出:

     state 0x10, keycode 115 (keysym 0xff56, Next), same_screen YES,
    

    这里我们感兴趣的部分是 keycode 115Next - 行为的名称。

  3. 现在当你想要交换这两个键时,使用 xmodmap

      xmodmap -e "keycode 110 = Next"
    

    这将把键码为 110 的键映射为动作 Next。非常简单。

    请注意,如果你映射的键在与 Shift 键一起使用时应该有不同的含义(例如对于英国键盘布局,Shift+2 会得到引号),那么你可以在第一个命令后面列出次要命令。例如,如果你希望代码为 53 的键在正常情况下映射为反斜杠,但在与 Shift 键一起使用时映射为竖线符号,你可以这样做:

      xmodmap -e "keycode 53 = backslash bar"
    

附加信息:这些映射的顺序取决于键盘布局。通常是 KeyShift+Keymode_switch+Keymode_switch+Shift+KeyAltGr+KeyAltGr+Shift+Key,但对于更特殊的布局(如德国的Neo 2),可能会有很大的不同。要跳过一列,请使用NoSymbol。这里有一个所有键符号的综合列表
您可以通过在/usr/share/X11/xkb/symbols/中的文件中找到您的布局来查看具体的顺序。

注意:这些更改仅适用于活动的X会话,并且在重新启动后将丢失。 如果您想永久保存更改,您必须在上述命令之后运行以下命令:

xmodmap -pke >~/.Xmodmap

(它在你的主目录(~)中创建一个名为.Xmodmap的文件)

然后你需要在你的主目录中创建一个名为.xinitrc的文件,在其中放置命令xmodmap .Xmodmap

您现在可以修改.Xmodmap并从控制台运行xmodmap .Xmodmap立即查看更改。在.Xmodmap中的更改将持久保存。

来源: Ubuntu论坛

额外内容:

如果你要重新映射的按键在不同状态下有不同的行为(例如数字键盘上的键取决于NumLock),你只需执行xmodmap -pm以获取修饰符列表,然后执行以下操作:

xmodmap -e "KEYCODE MODIFIER = 行为 带有修饰符的行为"

假设,例如,您想在数字键盘上获得一个句点而不是逗号(对大多数程序员很有用),但是当NumLock关闭时,您希望保留“删除”行为。
xmodmap -e "keycode 91 mod2 = KP_Delete period"

因为 `xmodmap -pm` 告诉我们 `mod2` 是 `Num_Lock`,其他名称是通过在 `xev` 中按下键来获得的。

5xev 无法捕捉到 Fn 键的按下事件 - om-nom-nom
3选择的答案在Ubuntu 12.10上无法重新映射大写锁定键。我通过转到系统设置 -> 键盘 -> 布局设置 -> 选项来实现了这一点,那里有一个键和替代行为的列表。在Unity和终端中都运行得很完美。 - Allyl Isocyanate
1如果您连接了多个键盘,这些工具能否区分另一台键盘上的相同按键? - jobukkit
@Jop 我不这么认为,因为你所改变的是对按键码的响应,而两个键盘的按键码可能会重叠。 - John C
1奇怪,我做了所有的事情,但是在重新启动后我的按键映射仍然重置了。 - Costa Michailidis
1我不认为在Ubuntu 12.04中有一个-event标志。它会抛出一个错误,并且在man xev中没有提到"event"。 - isomorphismes
顺便说一句:在将xev的结果记录到名为event.out的文件后,我使用perl -e '$/ = "\n\n"; while(<>) { print $_ if $_ =~ "KeyPress"; }' event.out > keypress.event.out来过滤非按键事件。 - isomorphismes
1xmodmap -e "KEYCODE MODIFIER = behaviour behaviour_with_modifier" ... 你从哪里找到这个命令的?它似乎不起作用,我也找不到其他关于这个语法的参考文档。 - Martin Tournoij
这个答案对于Ubuntu 14.04仍然适用,并且完全正常,你只需要每次登录时运行这些命令即可。 - jmiserez
1
  1. 如果 .xinitrc 不起作用,请尝试使用 .xsessionrc;
  2. 避免 setxkbmap 覆盖 xmodmap:http://askubuntu.com/questions/451945
- updogliu
Ubuntu Server 18.04,不带LightDM/GDM和桌面环境,只需在.xinitrc中键入startxxmodmap -exmodmap即可正常工作。 - frozen-flame
我可以使用命令xmodmap -e 'keycode 67='来禁用功能键,比如F1。但是如果需要禁用一个组合键,比如"Control Alt F1",应该怎么做呢? - Subhajit
@om-nom-nom:Fn是一个特殊的硬件按键,它的处理应该由低级软件(如固件)来完成,即使没有操作系统运行或者官方不支持也能正常工作。重新映射它是可行的,但需要修改相应的固件。希望这个固件是开源的,如果机主明智的话。 :) - anon
1@Subhajit:如果键码67不再映射到F1,那么就没有办法再触发Control-Alt-F1的按键组合了,也没有任何包含F1的其他组合键,因为没有F1键可以按下,除非有另一个键盘或者将另一个键码映射到F1。 - anon
如果你想重新映射键盘按键或鼠标按钮到特定的键位,可以使用sezanzeb开发的"Input Remapper"。它非常简单易用,有图形界面,并且功能稳定可靠。我刚刚设置了一个特定的快捷键来模拟键盘按键,效果很好。 - Allexj

如果您想要移动Shift键,有几个额外的步骤:
 xmodmap -e "keycode 62 = Up" # Shift => Up
 xmodmap -e "keycode 111 = Shift_R" # Up => Shift
 xmodmap -e "add shift = Shift_R" # Make the new Shift key actually do shifting
 xmodmap -e "remove shift = Up" # Prevent the old Shift key from shifting
 xset r 62 # Make the new Up key autorepeat
 xset -r 111 # Prevent the new Shift key from autorepeating

+1 对于修饰符的所有答案中,这是最像 Unix 的一个(我在很久以前就已经用我的 iBook 2 这样做过了,当时我希望回车键拥有 Control_R 的功能,但是细节已经忘记了)。 - rbrito
3联想笔记本电脑键盘用户的重要答案 - Einar Ólafsson
由于xmodmap专门编程以允许修改键的交换,根据man页面的说明,这应该可以原子化地完成。 - anon

我刚有个灵感.. 我觉得你可能对“重新映射”有完全不同的理解.. 但是我会保留我的答案...(我不知道如何将一个键重新分配为另一个键)
更新:我的“灵感”已经得到证实;(我回答了错误的问题 :)... 请参阅NES的社区维基答案(上面已接受)。
有两种常见的方法可以重新绑定按键。
- 局部绑定到特定程序 (在不同的应用程序/窗口中,一个按键可以用于不同的功能) - 全局绑定给特定用户 (一个按键在所有窗口中具有相同的功能)
对于“局部绑定到程序”的方法,有时候应用程序本身提供了更改按键绑定的方式... 例如:
Firefox有一个名为keyconfig的插件... 有关详细信息,请参阅MozillZine的帖子。
大多数Ubuntu程序都是基于Gnome的,而且有一个特定的工具可以修改这些Gnome应用程序的菜单项绑定...它被称为“可编辑的菜单加速器”...这是一个非常敏感的工具,但相当强大...你可以通过运行“gconf-editor”(通过终端或Alt+F2)来启用它...导航到“desktop”--“gnome”--“interface”,然后选择“can_change_accels”....然后,您可以将菜单项更改为任何您喜欢的虚拟内容(每个程序/窗口)...我建议您在完成所需操作后立即禁用它。
否则,您可以设置全局热键。我使用一个叫做xbindkeys的程序Install xbindkeys,还有一个可通过主菜单--首选项中的“键盘快捷键”选项进行设置。
如果你使用xbindkeys,你需要将它添加到你的"启动应用程序"中(主菜单--首选项)... 此外(根据Stefano Palazzo的建议),我之前在askubuntu页面上的一个回答中写了一个更详细的关于xbindkeys的描述。

+1,非常好!一个建议:你应该将你的另一个问题中的优秀回答整合到这个问题中,并且可以简单解释一下xbindkeys的配置格式。 - Stefano Palazzo
1关于分配其他按键的部分可以通过xdotool来处理,查看一下man手册,我已经用它解决了这个问题 - Stefano Palazzo
我最近发现了另一种方法,使用一个叫做xmodmap的工具和一个叫做xev的工具结合起来。我不知道xmodmap和xbindkeys之间的区别是什么,但对我来说这个方法非常有效。关于这两者中更好的工具,你有什么建议吗?这里有一个非常有帮助的逐步指南,描述了xmodmap和xev的用法:http://ubuntuforums.org/showpost.php?p=7675138&postcount=2 - NES
@NES.. 看起来我理解错了重点.. 你似乎想要重新映射操作系统对特定按键的解释,也就是非标准键盘解释,而我一直在提到标准键盘解释,并且只是重载默认的按键分配。(我觉得你想要的类似于交换鼠标左右键).. 嗯,看来我的回答对错了事情 :) - Peter.O
是的,但没有问题。答案也很有趣。我上面发布的逐步指南链接是正确的方法。所以我会发表一个简短的答案,让其他用户有一个好的指南。谢谢Fred。 - NES
键盘快捷方式提示对我来说非常完美。虽然我的需求相当简单,我只是想打开文件管理器,在启动器下有一个“主文件夹”设置选项。 - eksortso

这是我尝试将 ENTER 键映射为 SHIFT 键(反之亦然)的方法:
$ uname -a

报告:
Linux box 2.6.32-37-generic #81-Ubuntu SMP Fri Dec 2 20:35:14 UTC 2011 i686 GNU/Linux

$ which xmodmap

报告:
/usr/bin/xmodmap

$ which xev

报告:
/usr/bin/xev

$ xev

忽略接下来的五十行或者更多行。
PRESS THE ENTER KEY (notice the third line):
KeyPress event, serial 33, synthetic NO, window 0x5600001,
    root 0x110, subw 0x0, time 263441120, (738,242), root:(771,314),
    state 0x0, keycode 36 (keysym 0xff0d, Return), same_screen YES,
    XLookupString gives 1 bytes: (0d)
    XmbLookupString gives 1 bytes: (0d)
    XFilterEvent returns: False

KeyRelease event, serial 33, synthetic NO, window 0x5600001,
    root 0x110, subw 0x0, time 263441271, (738,242), root:(771,314),
    state 0x0, keycode 36 (keysym 0xff0d, Return), same_screen YES,
    XLookupString gives 1 bytes: (0d)
    XFilterEvent returns: False

PRESS THE SHIFT KEY (notice the third line):
KeyPress event, serial 30, synthetic NO, window 0x5600001,
    root 0x110, subw 0x0, time 263592202, (464,368), root:(497,440),
    state 0x0, keycode 62 (keysym 0xffe2, Shift_R), same_screen YES,
    XLookupString gives 0 bytes:
    XmbLookupString gives 0 bytes:
    XFilterEvent returns: False

KeyRelease event, serial 33, synthetic NO, window 0x5600001,
    root 0x110, subw 0x0, time 263592298, (464,368), root:(497,440),
    state 0x1, keycode 62 (keysym 0xffe2, Shift_R), same_screen YES,
    XLookupString gives 0 bytes:
    XFilterEvent returns: False

每次按键的第三行是最重要的。
FOR:
state 0x0, keycode 36 (keysym 0xff0d, Return), same_screen YES,
The name "Return" is the name of the behavior of the key pressed.
The number of the key pressed is "36".

state 0x0, keycode 62 (keysym 0xffe2, Shift_R), same_screen YES,
The name "Shift_R" is the name of the behavior of the key pressed.
The number of the key pressed is "62".

反转映射:

$ xmodmap -e "keycode 62 = Return"
$ xmodmap -e "keycode 36 = Shift_R"

保存结果:

$ xmodmap -pke > ~/.Xmodmap
$ vi ~/.xinitrc

添加

 xmodmap ~/.Xmodmap

$ sudo reboot

主要问题是反向操作没有起作用。 ENTER键被映射到SHIFT_R键;但是SHIFT_R键没有映射到ENTER键。你猜怎么回事。

为了在不依赖于X的情况下进行全局重映射,您可以使用console-setup(5)。
在我的情况下,我想将Caps Lock键重新映射为D键,因为我的D键坏了 :)
首先,我使用dumpkeys(1)获取了一个映射模板,在D键的情况下,有趣的部分是键码32的映射(在我的键盘上);请注意grep模式中有两个空格!
$ sudo dumpkeys | grep "keycode  32" > tempfile
$ cat tempfile
keycode  32 = +d
    shift   keycode  32 = +D
    altgr   keycode  32 = +eth
    shift   altgr   keycode  32 = +ETH
    control keycode  32 = Control_d
    shift   control keycode  32 = Control_d
    altgr   control keycode  32 = Control_d
    shift   altgr   control keycode  32 = Control_d
(121 lines total...)

为了将地图调整适用于我的键盘上的Caps Lock(键码58),我们需要进行一些更改。
sed 's/32/58/' -i tempfile

现在它读起来是这样的
keycode  58 = +d
    shift   keycode  58 = +D
    altgr   keycode  58 = +eth
(etc...)

要将此重新映射添加到默认映射中,只需将其附加到“console-setup”重新映射包含文件中即可。
sudo sh -c 'cat tempfile >> /etc/console-setup/remap.inc'

需要重新配置 console-setup(使用 -phigh 跳过低优先级问题)
sudo dpkg-reconfigure console-setup -phigh

现在重新映射应该已经完成,并且会在启动时自动加载。

我花了整整一天的时间来尝试创建一个Ctrl+Pageup的快捷方式。
我首先尝试了xmodmap,但它无法生成修饰符事件。因此,无法创建一个例如Control事件的快捷方式。
然后我尝试了xbindkeysxmacro。这种方法基本上可以工作,但是xbindkeys无法捕获我的系统上的某些键盘组合,比如Alt + ___
所以最后我使用了Unity自带的键盘快捷方式->自定义快捷方式来设置我的快捷方式。
而且我现在使用了xvkbd程序来生成键盘事件,但这只是个人偏好。无论是xmacro还是xvkbd都几乎一样。另一个额外的提示是在xmacroxvkbd中添加延迟参数,以确保事件不会丢失。

对我来说,软件中心的AutoKey效果最好。它有直观的图形用户界面,要添加新的绑定,点击“新建”->“短语”。
  1. 添加名称,点击确定
  2. 在“短语设置”部分,确保粘贴方式设置为键盘
  3. 添加您想要使用的热键
  4. 将命令添加到文本字段中,例如模拟左箭头键-可以使用(特殊键的列表在这里)。

这个方法完美地设置了Super+CSuper+VCtrl+CCtrl+V。当在Mac上通过VirtualBox使用Ubuntu时,这是一个完美的解决方案。 - user1
然而,这并不会禁用其他监听这些快捷键的命令。要禁用Super+V的“显示通知列表”功能,请转到设置->设备->键盘,并在那里禁用它。 - user1
@Ben没错,如果它能显示其他应用的绑定就太好了,但这只是锦上添花而已。我的问题是无法重新映射“Fn”键。我决定试试uhk键盘-这个独立于操作系统的可编程设备,看看效果如何。 - ego

您也可以尝试使用 https://github.com/sezanzeb/input-remapper
它在后台运行并主动注入映射的按键码。
虽然这增加了一些额外的灵活性,但如果你只需要重新映射一个按钮并完成任务,你可以尝试使用xmodmap。看起来已经有人详细解释过了。
或者编写一个xkb的“symbols”文件并使用setxkbmap,但我不推荐这样做。setxkbmap也可以按设备进行使用。
对我来说,编写xkb或xmodmap配置会导致麻烦,尤其是如果两个设备报告相同的键码,即使配置将它们映射到不同的字符,同时按下它们(这也是我不得不为此编写注入工具的主要原因)。
最近我找到了这个解决方案:https://www.reddit.com/r/linux_gaming/comments/k3h9qv/remapping_keys_using_hwdb_files/

1忍不住要再加一条评论来说一下键位映射器有多棒!重新映射快捷键现在真是太愉快了!非常感谢!它绝对应该更受欢迎。 - DMT
1忘记添加,它甚至可以与Wayland一起使用! - DMT

如果您只需要为特定程序重新映射一个键,我刚刚在hax11中添加了这个功能。

我想将 PgUp 改为 Home 或者将 PgDown 改为 End

要使用hax11进行此操作,请打开程序的配置文件,位于~/.config/hax11/profiles目录下(例如:~/.config/hax11/profiles/usr\lib\firefox\firefox),然后添加以下内容:
Enable=1
MapK112=K110
MapK117=K115

通过.desktop快捷方式进行非持久性重映射

在我的情况下,我想将小键盘的小数点重新映射为逗号,但只是暂时的,所以 xmodmap 在我的情况下满足了这个要求

  • 找出要重新映射的特定按键的键码和我想要映射的键符号
    • :~$ xev | grep keycode
  • 按下感兴趣的按键并监视标准输出
    state 0x10, keycode 91 (keysym 0xffae, KP_Decimal), same_screen YES,
    XKeysymToKeycode returns keycode: 129
    state 0x10, keycode 59 (keysym 0x2c, comma), same_screen YES,
  • 首先,我按下我想重新映射的按键,然后按下我想映射到的按键。

    • 我想重新映射的按键的键码: ...,键码 91(...
    • 我想将该键码映射到的按键符号: ...(键符号 0x2c,逗号),...
  • 查找要重新映射的键码的当前映射:

    • :~$ xmodmap -pke | grep "keycode\s*91"
keycode  91 = KP_Delete KP_Decimal KP_Delete KP_Decimal

根据xmodmap的manpage中的规定:一个按键可以附加多达八个keysym..:当没有修饰键时使用第一个keysym,与Shift一起使用第二个,依此类推... 这与我的情况不完全对应,但通过试错我发现第二个位置参数是我想要的
  • 映射到新配置。

    • :~$ xmodmap -e "keycode 91 = KP_Delete comma KP_Delete KP_Decimal"
  • 映射回原始配置。

    • :~$ xmodmap -e "keycode 91 = KP_Delete KP_Decimal KP_Delete KP_Decimal"
  • 我在~\.local\share\applications\中创建了一对.desktop快捷方式,以便轻松切换到其中一个配置或另一个配置:

[Desktop Entry]
Name=KeyPad comma
Exec=xmodmap -e "keycode 91 = KP_Delete comma KP_Delete KP_Decimal"
Terminal=false
Type=Application
Icon=/home/paui/.icons/KP_comma.png

额外信息: state field是xev命令的输出结果。
state field是当前活动修饰键的“总和”。以下是我解读出来的修饰键。
    0x01 Shift
    0x02 Caps
    0x04 Control
    0x08 Alt
    0x10 NumLock
    0x80 Alt Gr