如何使所有应用程序尊重我修改后的 xkb 布局?

non*_*pop 17 linux keyboard-layout xkb

我不喜欢在主键盘和移动键之间跳转,所以我在我的 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      ] };
}
Run Code Online (Sandbox Code Playgroud)

然后我稍后将这些包含在文件中的布局中。现在我应该可以通过 AltGr + j,k,l,i(或 h,t,n,c,因为我使用的是 dvorak)等来访问光标键。这在许多情况下都有效(如 Firefox、urxvt、Eclipse、 LyX 的主要文本区域),但是当我尝试使用这些“快捷方式”(如 NetBeans 和 LyX 对话框)移动光标时,有些程序什么也不做。

那么,有没有办法让这些其他程序也尊重我的意愿?为什么他们一开始就不工作?我没有使用 DE;只有真棒WM。

编辑:

  • 是一个完整但简化的键盘布局文件。我有这个/usr/share/X11/xkb/symbols/nonpop,我用setxkbmap nonpop.
  • 我注意到在 MonoDevelop 中移动工作但选择没有。也就是说,如果我按 Shift+Right 然后像往常一样选择文本,但是如果我按 AltGr+Shift+n 那么光标只是移动而不选择。例如在 Firefox 中,两种方式都可以用于选择。
  • 这里到底只讲覆盖,看起来像的东西,或许是一个解决方案,但我还没有想出如何使用它们。

Che*_*hel 19

为什么他们不工作

根据您提到的ArchWiki 文章

  • X 服务器从输入设备获取键码并将它们转换为 statekeysym

    • state是 X 修饰符(Ctrl/Shift/等)的位掩码。

    • keysym是(根据/usr/include/X11/keysymdef.h)的整数

      识别与键盘布局的每个键(例如,通过可见雕刻)相关联的字符或功能。

      每个可打印字符都有自己的键符,例如plus, a, A, or Cyrillic_a,但其他键也会生成它们的键符,例如 Shift_L, Leftor F1

  • 按键按下/释放事件中的应用程序获取所有这些信息。

    一些应用程序像Control_L自己一样跟踪键符,其他应用程序只是在state 中查找修饰符位。

那么当你按下AltGr+时会发生什么j

  • 你按AltGr。应用程序使用键码 108 ( <RALT>) 和键符 0xfe03 ( ISO_Level3_Shift)获取 KeyPressed 事件,状态为 0。

  • 您按下j(在没有修饰符的情况下映射到 dvorak 中的“h”)。应用程序使用键码 44 ( <AC07>)、键符 0xff51 ( Left) 和状态 0x80(修改器 Mod5 开启)获取 KeyPressed 事件。

  • 你释放j。应用程序使用相同的参数获取键<AC07>/ 的KeyRelease 事件 Left

  • 然后释放AltGr——AltGr 的 KeyRelease 事件。(顺便说一下,这里的状态仍然是 0x80,但这并不重要。)

如果您运行xev实用程序,则可以看到这一点。

所以,这一切都意味着,虽然应用程序Left从普通 key获取相同的键符代码 ( ) <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)" };
};
Run Code Online (Sandbox Code Playgroud)

现在,如果您先按下AltGr然后(单独)一个移动键,这应该可以工作。

但是,这不是很有用,更合适的是LockGroup 在组切换前后而不是闩锁并按AltGr。更好的可能是SetGroup- 然后 AltGr 只会在被按下时选择该组,但这会向应用程序公开 AltGr 的键符(ISO_Group_Shift//ISO_Group_Latch定义的任何内容)(但修饰符状态保持干净)。

但是......还有一种可能性是应用程序还读取键码(真实键的代码)。然后它会注意到“假”光标键。

2.叠加

更“低级”的解决方案是覆盖(如同一篇文章 所述)。

覆盖只是意味着某些(真实键盘)键返回另一个键的键码。X 服务器更改键的键码并计算该新键码的修饰符状态和键符,因此应用程序不应注意到更改。

但是叠加非常有限:

  • X 服务器中只有 2 个覆盖控制位(即最多可以有 2 个覆盖)。
  • 每个键只能有 1 个备用键码。

至于其余的,实现与具有单独组的方法非常相似:

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)" };
};
Run Code Online (Sandbox Code Playgroud)

SetControls意味着在按键被按下时改变控制位并在按键释放时恢复它。应该有类似的功能LatchControls,但 xkbcomp给了我

Error:            Unknown action LatchControls
Run Code Online (Sandbox Code Playgroud)

关于键盘映射编译。

(顺便说一下,我也使用了 dvorak 并且还重新映射了一些移动键符到高级字母键。并且还遇到了一些损坏的功能(在 Xfce 笔记中选择和通过 Ctrl-Alt-Left/Right 切换桌面)。感谢你的问题和这个答案,现在我知道覆盖是什么:))


小智 6

如何使它们工作 - 解决方案 3

使用附加级别和操作重定向键

以下解决方案使用左 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)" 
    };
};
Run Code Online (Sandbox Code Playgroud)


Ale*_*nko 5

我也有同样的问题。这太痛苦了。

所以标题是“如何让所有应用程序尊重我修改后的 xkb 布局?”。好吧,我认为唯一的方法是修复所有错误执行的程序。让我们这样做!

好吧,在报告NetBeans中的错误之后(更新:我已经尝试了最新版本,现在可以使用了!),我想我会继续为每个应用程序报告这个错误。名单上的下一个应用程序是Speedcrunch

但是,在寻找类似的错误报告后,我发现了这个问题。别人也有同样的问题,太好了!

阅读评论后,您将了解此错误应该存在于所有 QT 应用程序中。这是一个QT 错误报告。未解决,但问题似乎已在 Qt5 中解决

但是,如果您查看评论,则有一种解决方法!下面是它的工作原理。如果你这样做:

key <SPCE> { [ ISO_Level3_Shift ] };
Run Code Online (Sandbox Code Playgroud)

然后你可以把它改成这样:

key <SPCE> {
  type[Group1]="ONE_LEVEL",
  symbols[Group1] = [ ISO_Level3_Shift ]
};
Run Code Online (Sandbox Code Playgroud)

它实际上会解决一些应用程序的问题!例如,Speedcrunch现在对我有用!好极了!

概括

现在任何应用程序都应该可以正常工作。如果没有,那么你必须使用type[Group1]="ONE_LEVEL". 如果您已经拥有它,那么您必须更新您的软件。如果它仍然不起作用,那么它是特定于应用程序的,您必须提交错误报告。

更新 (2017-09-23)

截至今天,所有应用程序都尊重我的键盘布局。除了一个。

说真的,Chromium 中的键盘处理是垃圾。它有几个问题:

  • Shift 选择不使用自定义箭头键(但箭头键本身可以正常工作)
  • 如果您有多个布局,并且其中一个布局上的某些键是特殊的(例如箭头、退格键等),那么在另一个布局上,此键将固定为您在第一个布局上拥有的任何键。例如,如果您有两个布局:foobar并且某些键在 中执行 Backspace foo,那么bar即使在那里重新定义它,它也会继续作为 Backspace in 工作。

多年来,我只是通过不使用铬来忽略这些问题。然而,现在人们倾向于使用Electron,不幸的是它建立在 Chromium 上。

解决此问题的正确方法是在 Chromium 中提交错误报告并希望最好。我不知道他们需要多长时间才能解决一个只影响几个用户的问题……但这似乎是唯一的出路。问题在于,铬实际上与neo(de)布局配合得很好。Neo 布局在 level5 上有箭头键,但我无法让它在我的自定义布局中工作。

仍然打开的错误报告: