如何让它们发挥作用 - 解决方案 3

如何让它们发挥作用 - 解决方案 3

我不喜欢在主键盘和移动键之间跳转,所以我在我的 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。

编辑:

  • 这里是完整但简化的键盘布局文件。我有这个文件,/usr/share/X11/xkb/symbols/nonpop我用它加载它setxkbmap nonpop
  • 我注意到在 MonoDevelop 中移动有效但选择无效。也就是说,如果我按 Shift+Right,则文本会照常被选中,但如果我按 AltGr+Shift+n,则光标只会移动而不会被选中。例如,在 Firefox 中,两种方式都可用于选择。
  • 这里最后他们谈论的是覆盖这看起来也许可以作为一个解决方案,但我还不知道如何使用它们。

答案1

为什么它们不工作

根据ArchWiki 的文章你提到的:

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

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

    • 键符是(根据/usr/include/X11/keysymdef.h)整数

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

      每个可打印字符都有自己的键符,如plusaACyrillic_a,但其他键也会生成自己的键符,如 Shift_LLeftF1

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

    有些应用程序会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. 单独分组

我想到唯一方法是:将光标移动键定义在一个单独的组中,然后在按下jklihtnc) 键之前通过单独的按键切换到该组(据我所知,组锁存是一次性组更改的首选方法)。

例如:

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 选择不能通过自定义箭头键进行(但箭头键本身可以正常工作)
  • 如果您有多个布局,并且其中一个布局上的某个键是特殊的(例如箭头、退格键等),那么在另一个布局上,此键将固定为第一个布局上的键。例如,如果您有两个布局:foobar并且某个键在 中执行退格键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 应用程序也是如此。一个缺点是您必须准确指定输入设备,而不是说“任何键盘”。另一个潜在的缺点是,任何其他试图获取键盘的进程都无法做到这一点。

相关内容