我不喜欢在主键盘和移动键之间跳转,所以我在我的 xkb 布局文件中添加了以下内容。
hidden partial xkb_symbols "movement"
{
key <AD08> { [ NoSymbol, NoSymbol, Up, Up ] };
key <AC08> { [ NoSymbol, NoSymbol, Down, Down ] };
key <AC07> { [ NoSymbol, NoSymbol, Left, Left ] };
key <AC09> { [ NoSymbol, NoSymbol, Right, Right ] };
key <AD09> { [ NoSymbol, NoSymbol, Prior, Prior ] };
key <AB09> { [ NoSymbol, NoSymbol, Next, Next ] };
key <AB07> { [ NoSymbol, NoSymbol, Home, Home ] };
key <AB08> { [ NoSymbol, NoSymbol, End, End ] };
key <AC06> { [ NoSymbol, NoSymbol, Delete, Delete ] };
}
然后我稍后在文件中将这些添加到布局中。现在我应该可以通过 AltGr + j、k、l、i(或 h、t、n、c,因为我使用的是 dvorak)等访问光标键。这在许多情况下都有效(例如 Firefox、urxvt、Eclipse、LyX 的主文本区域),但当我尝试使用这些“快捷方式”移动光标时,某些程序没有任何反应(例如 NetBeans 和 LyX 对话框)。
那么,有没有办法让这些其他程序也尊重我的意愿?为什么它们一开始就不起作用?我没有使用 DE;只使用 Awesome WM。
编辑:
答案1
为什么它们不工作
根据ArchWiki 的文章你提到的:
X 服务器获取键码从输入设备并将它们转换为 状态和键符。
状态是 X 修饰符(Ctrl/Shift/等)的位掩码。
键符是(根据
/usr/include/X11/keysymdef.h
)整数识别与键盘布局的每个键相关的字符或功能(例如,通过可见的雕刻)。
每个可打印字符都有自己的键符,如
plus
、a
、A
或Cyrillic_a
,但其他键也会生成自己的键符,如Shift_L
、Left
或F1
。
按键按下/释放事件中的应用程序会获取所有这些信息。
有些应用程序会
Control_L
自行跟踪键盘符号,而有些应用程序则会在状态。
那么,当你按下AltGr+时会发生什么j:
您按下AltGr。应用程序获取 KeyPressed 事件,其键码为 108 (
<RALT>
),键符为 0xfe03 (ISO_Level3_Shift
),状态为 0。您按下j(在不带修饰符的 dvorak 中映射为“h”)。应用程序获取 KeyPressed 事件,其键码为 44 (
<AC07>
),键符为 0xff51 (Left
),状态为 0x80(修饰符 Mod5 已打开)。您释放。应用程序将获取具有相同参数的j键
<AC07>
/的 KeyRelease 事件。Left
然后释放AltGr——AltGr 的 KeyRelease 事件。(顺便说一下,这里的状态仍然是 0x80,但这并不重要。)
如果你运行实用程序,你就会看到这一点xev
。
因此,这意味着,尽管应用程序Left
从普通键获取相同的键符代码 ( ) <LEFT>
,但它也从 AltGr 获取键符代码和修饰符状态。最有可能的是,那些不起作用的程序会监视修饰符,并且在某些修饰符处于活动状态时不想工作。
如何让它们发挥作用
显然,我们无法将每个程序都改为不寻找修饰符。那么摆脱这种情况的唯一选择就是不生成修饰符的键符和状态位。
1. 单独分组
我想到唯一方法是:将光标移动键定义在一个单独的组中,然后在按下j、k、l、i(h
、
t
、n
、c
) 键之前通过单独的按键切换到该组(据我所知,组锁存是一次性组更改的首选方法)。
例如:
xkb_keymap {
xkb_keycodes { include "evdev+aliases(qwerty)" };
xkb_types { include "complete" };
xkb_compatibility {
include "complete"
interpret ISO_Group_Latch { action = LatchGroup(group=2); };
};
xkb_symbols {
include "pc+us(dvorak)+inet(evdev)"
key <RALT> { [ ISO_Group_Latch ] };
key <AC07> {
type[Group2] = "ONE_LEVEL",
symbols[Group2] = [ Left ]
};
key <AC08> {
type[Group2] = "ONE_LEVEL",
symbols[Group2] = [ Down ]
};
key <AC09> {
type[Group2] = "ONE_LEVEL",
symbols[Group2] = [ Right ]
};
key <AD08> {
type[Group2] = "ONE_LEVEL",
symbols[Group2] = [ Up ]
};
};
xkb_geometry { include "pc(pc104)" };
};
现在,如果您先按下AltGr然后(单独)按下其中一个移动键,它应该可以工作。
但是,这不太有用,更合适的做法是,LockGroup
在切换组之前和之后按下 AltGr,而不是锁定。更好的做法可能是SetGroup
— AltGr 只会在被按下时选择该组,但这会向应用程序公开 AltGr 的键符(ISO_Group_Shift
/ ISO_Group_Latch
/无论定义什么)(但修饰符状态保持干净)。
但是... 还有一种可能性是应用程序也会读取键码(真实按键的代码)。然后它会注意到“假”光标键。
2. 覆盖
更“低级”的解决方案是覆盖(同样文章 描述)。
覆盖只是意味着某个(真实键盘)键返回另一个键的键码。X 服务器会更改某个键的键码并计算该新键码的修饰符状态和键符,因此应用程序不会注意到该更改。
但覆盖范围非常有限:
- X 服务器中只有 2 个覆盖控制位(即最多可以有 2 个覆盖)。
- 每个键只能有一个备用键码。
至于其余部分,其实现方式与单独组的方法非常相似:
xkb_keymap {
xkb_keycodes { include "evdev+aliases(qwerty)" };
xkb_types { include "complete" };
xkb_compatibility {
include "complete"
interpret Overlay1_Enable {
action = SetControls(controls=overlay1);
};
};
xkb_symbols {
include "pc+us(dvorak)+inet(evdev)"
key <RALT> {
type[Group1] = "ONE_LEVEL",
symbols[Group1] = [ Overlay1_Enable ]
};
key <AC07> { overlay1 = <LEFT> };
key <AC08> { overlay1 = <DOWN> };
key <AC09> { overlay1 = <RGHT> };
key <AD08> { overlay1 = <UP> };
};
xkb_geometry { include "pc(pc104)" };
};
SetControls
意思是在按下键时更改控制位,并在释放键时恢复控制位。应该有类似的功能LatchControls
,但
xkbcomp
我
Error: Unknown action LatchControls
关于键盘映射编译。
(顺便说一句,我也使用 dvorak,并将一些移动键重新映射到高级别的字母键。并且还遇到了一些损坏的功能(在 Xfce 注释中进行选择以及通过 Ctrl-Alt-Left/Right 进行桌面切换)。感谢您的问题和这个答案,现在我知道什么是覆盖 :)。)
答案2
如何让它们发挥作用 - 解决方案 3
使用附加级别和 Action RedirectKey
以下解决方案使用左 Alt 键在 jkli 上提供光标键、在 uopö 上提供 Home/End/PageUp/PageDown 以及在 Backspace 上提供 Delete 键。
左 Alt 键仍可用于所有其他键的其他用途(例如应用程序菜单)。使用光标块时,左 Alt (Mod1) 将从修饰符状态中移除,因此应用程序看不到它。
xkb_keymap {
xkb_keycodes {
include "evdev+aliases(qwertz)"
};
xkb_types {
include "complete"
};
xkb_compat {
include "complete"
interpret osfLeft {
action = RedirectKey(keycode=<LEFT>, clearmodifiers=Mod1);
};
interpret osfRight {
action = RedirectKey(keycode=<RGHT>, clearmodifiers=Mod1);
};
interpret osfUp {
action = RedirectKey(keycode=<UP>, clearmodifiers=Mod1);
};
interpret osfDown {
action = RedirectKey(keycode=<DOWN>, clearmodifiers=Mod1);
};
interpret osfBeginLine {
action = RedirectKey(keycode=<HOME>, clearmodifiers=Mod1);
};
interpret osfEndLine {
action = RedirectKey(keycode=<END>, clearmodifiers=Mod1);
};
interpret osfPageUp {
action = RedirectKey(keycode=<PGUP>, clearmodifiers=Mod1);
};
interpret osfPageDown {
action = RedirectKey(keycode=<PGDN>, clearmodifiers=Mod1);
};
interpret osfDelete {
action = RedirectKey(keycode=<DELE>, clearmodifiers=Mod1);
};
};
xkb_symbols {
include "pc+de(nodeadkeys)"
include "inet(evdev)"
include "compose(rwin)"
key <LALT> {
type[Group1] = "ONE_LEVEL",
symbols[Group1] = [ ISO_Level5_Shift ]
};
modifier_map Mod1 { <LALT> };
key <AC07> {
type[Group1] = "EIGHT_LEVEL_SEMIALPHABETIC",
symbols[Group1] = [ j, J, dead_belowdot, dead_abovedot, osfLeft, osfLeft, osfLeft, osfLeft ]
};
key <AC08> {
type[Group1] = "EIGHT_LEVEL_SEMIALPHABETIC",
symbols[Group1] = [ k, K, kra, ampersand, osfDown, osfDown, osfDown, osfDown ]
};
key <AC09> {
type[Group1] = "EIGHT_LEVEL_ALPHABETIC",
symbols[Group1] = [ l, L, lstroke, Lstroke, osfRight, osfRight, osfRight, osfRight ]
};
key <AC10> {
type[Group1] = "EIGHT_LEVEL_SEMIALPHABETIC",
symbols[Group1] = [ odiaeresis, Odiaeresis, doubleacute, doubleacute, osfPageDown, osfPageDown, osfPageDown, osfPageDown ]
};
key <AD07> {
type[Group1] = "EIGHT_LEVEL_SEMIALPHABETIC",
symbols[Group1] = [ u, U, downarrow, uparrow, osfBeginLine, osfBeginLine, osfBeginLine, osfBeginLine ]
};
key <AD08> {
type[Group1] = "EIGHT_LEVEL_SEMIALPHABETIC",
symbols[Group1] = [ i, I, rightarrow, idotless, osfUp, osfUp, osfUp, osfUp ]
};
key <AD09> {
type[Group1] = "EIGHT_LEVEL_ALPHABETIC",
symbols[Group1] = [ o, O, oslash, Oslash, osfEndLine, osfEndLine, osfEndLine, osfEndLine ]
};
key <AD10> {
type[Group1] = "EIGHT_LEVEL_ALPHABETIC",
symbols[Group1] = [ p, P, thorn, THORN, osfPageUp, osfPageUp, osfPageUp, osfPageUp ]
};
key <BKSP> {
type[Group1] = "EIGHT_LEVEL_ALPHABETIC",
symbols[Group1] = [ BackSpace, BackSpace, BackSpace, BackSpace, osfDelete, osfDelete, osfDelete, osfDelete ]
};
};
xkb_geometry {
include "pc(pc105)"
};
};
答案3
我也有同样的问题。太痛苦了。
所以标题是“如何让所有应用程序尊重我修改后的 xkb 布局?”。好吧,我认为唯一的方法是修复所有错误执行此操作的程序。让我们这样做吧!
好吧,在报告了该错误之后NetBeans(更新:我已经尝试了最新版本并且现在可以正常工作了!),我想我会继续为每个应用程序报告这个错误。列表中的下一个应用程序是速度冲击。
然而,在寻找类似的错误报告后,我发现这个问题。其他人也遇到了同样的问题,太好了!
阅读评论后,您将明白此错误应该存在于所有 QT 应用程序中。这是一个QT 错误报告。未解决,但似乎这个问题在 Qt5 中已经解决了。
但是,如果你看看评论,就会发现有一个解决方法!下面是它的工作原理。如果你这样做:
key <SPCE> { [ ISO_Level3_Shift ] };
然后你可以把它改成这样:
key <SPCE> {
type[Group1]="ONE_LEVEL",
symbols[Group1] = [ ISO_Level3_Shift ]
};
它确实能解决一些应用程序的问题!例如,速度冲击现在对我有用了!太棒了!
概括
目前,任何应用程序都应该可以正常工作。如果不能,那么您必须使用type[Group1]="ONE_LEVEL"
。如果您已经拥有它,那么您必须更新您的软件。如果它仍然不起作用,那么它就是特定于应用程序的,您必须提交错误报告。
更新(2017-09-23)
截至今天,所有应用程序都遵循我的键盘布局。除了一个之外。
严重地,Chromium 中的键盘处理很垃圾。它存在几个问题:
- Shift 选择不能通过自定义箭头键进行(但箭头键本身可以正常工作)
- 如果您有多个布局,并且其中一个布局上的某个键是特殊的(例如箭头、退格键等),那么在另一个布局上,此键将固定为第一个布局上的键。例如,如果您有两个布局:
foo
,bar
并且某个键在 中执行退格键foo
,那么即使在那里重新定义,它仍将继续作为 中的退格键工作bar
。
多年来,我一直忽略这些问题,只是不使用铬。然而,现在事情往往使用电子,不幸的是它是基于 Chromium 构建的。
解决此问题的正确方法是在 Chromium 中提交错误报告并希望获得最佳结果。我不知道他们需要多长时间才能解决仅影响几个用户的问题……但这似乎是唯一的出路。问题在于 Chromium 实际上可以很好地处理neo(de)
布局。Neo 布局在级别 5 上有箭头键,但我无法让它在我的自定义布局中工作。
仍未解决的错误报告:
答案4
最近的更新再次破坏了我的设置。这次我找到了一个完全不同的解决方案,到目前为止似乎有效:https://github.com/philipl/evdevremapkeys。此守护进程在键盘事件到达之前将其捕获任何其他处理程序,对它们进行转换,并将新事件发送到虚拟键盘设备,然后应用程序读取该设备。
为了使其正常工作,我从 xkb 布局文件中删除了所有移动键内容,并创建了以下内容evdevremapkeys.yaml
:
devices:
- input_name: Lenovo ThinkPad Compact USB Keyboard with TrackPoint
input_phys: usb-0000:03:00.0-8/input0
output_name: remap-kbd
remappings:
KEY_RIGHTALT:
- modifier_group: altgr
modifier_groups:
altgr:
KEY_J:
- KEY_LEFT
KEY_L:
- KEY_RIGHT
KEY_I:
- KEY_UP
KEY_K:
- KEY_DOWN
KEY_O:
- KEY_PAGEUP
KEY_DOT:
- KEY_PAGEDOWN
KEY_M:
- KEY_HOME
KEY_COMMA:
- KEY_END
KEY_H:
- KEY_DELETE
“nice” 应用程序似乎仍能正常工作,chrome 和 qt 应用程序也是如此。一个缺点是您必须准确指定输入设备,而不是说“任何键盘”。另一个潜在的缺点是,任何其他试图获取键盘的进程都无法做到这一点。