如何模拟数字键盘/小键盘以与设计软件(例如 Blender)一起使用?

如何模拟数字键盘/小键盘以与设计软件(例如 Blender)一起使用?

问题

使用某些软件时,例如搅拌机,能够使用数字键盘非常重要,因为用户可以在设计空间中进行定位等操作,但许多笔记本电脑没有配备物理数字键盘。由于常规数字栏输入(键盘顶部的 1-9)实际上对计算机代表不同的“符号”,因此在此类软件中通常具有完全不同的功能,这一事实使情况变得更加复杂。

尝试的解决方案

由于许多笔记本电脑没有配备数字键盘,许多平台上的常见解决方案是模拟小键盘,例如按住一个键的同时使用键盘上的其他键(例如 jkluio789 代表 123456789)。许多笔记本电脑在 BIOS 级别上实现此功能(例如使用 Fn 键)。然而,如果没有这样的低级实现,实现这种模拟是非常困难的。

网上有一些解决方案,但是它们通常不足以与设计软件一起使用(因为它们没有实现正确的符号,并且还需要使用修饰键),或者他们没有深入解释。大多数解决方案都集中在使用xkb,这是一个复杂且极难开始使用的架构。

良好解决方案的要求

解决这个问题的一个好办法是使用一个模拟键盘,图形软件会将其视为真正的键盘输入,并且易于使用。另一个限制是,Blender 会检测修饰键的使用(例如Shift,,(“Command”、“Windows 键”等),即使被告知了明确的修饰键,因此会将“按住修饰键时进行键盘模拟”解决方案解释为完全不同的输入(即 [ + ] 而不仅仅是)。因此,理想的解决方案实际上将涉及锁定机制(例如 Caps Lock 大写)而不是保持机制(例如 Shift 大写),这样就不会意外将修饰键传递给软件。AltSuperHyperxkbNumpad1AltNumpad1

答案1

快速开始

如果您不关心任何解释(我知道我可能会啰嗦),请按照{大括号中的粗体数字}在某些段落的开头。按顺序执行每个步骤,您可能可以在几分钟内实现此操作。请注意,本指南假定您具有一定的 Unix 能力(能够创建目录、创建文件、sudo提升到 root 访问权限等)。另请注意仅在指定的情况下才需要 root 访问权限sudo,所以除非被告知,否则您不需要使用。

解决方案的一般描述

我们将使用 xkb 向 Linux 添加“锁定”(如大写锁定)数字键盘模拟。我希望我的键“jkluio789”代表数字“123456789”的数字键盘表示,以及一些其他包含内容(“m,”->“0”,“-=[]”->[numpad]” -+*", "."->[小键盘]".")。我将使用 [ Shift+ Mod4+ [key]]组合键切换此“小键盘模式”,其中Mod4是我的操作系统键的修饰符代码(也称为“Command”或“Windows 键”,有时分配给Super或的修饰符代码Hyper) ,并且[key]是我的模拟小键盘中使用的任何键(例如“j”或“[”)。阅读完整的解决方案后,对此设置的简单修改应该相对简单。

为此,我们将定义一个自定义 xkb“类型”文件,它告诉 xkb 如何解释我们将用于初始化键盘模拟的各种修饰键,以及一个自定义 xkb“符号”文件,它告诉 xkb 每个我们按下的键应该表现正常(第 1 组),它在数字键盘模拟期间应该如何表现(第 2 组),以及如何在两者之间切换(两个组的级别 3 操作)。最后,我们将在每次启动新会话时将其集成到当前的 xkbmap 中,从而使我们的解决方案永久化sed(这样我们的解决方案就不会在每次xkb更新时被删除)。

解决方案详细说明

目录结构

{1}我们要做的第一件事是定义一个目录来保存各种文件。你的目录几乎可以是任何目录,但我的目录看起来像这样

/home
  +-<username>
    +-.xkb
      +-symbols
      +-types
      +-keymap
      +-log

类型文件

一旦我们有了目录树,就可以在我们的解决方案中定义实际的文件。我们要做的第一件事是定义我们的“类型”文件。该文件将告诉您xkb如何在“级别”之间移动(例如如何Shift将字母大写,从小写字母的第一级别移动到大写字母的大写级别)。这些级别有点难以掌握,特别是对于以英语为母语的人来说,但国际键盘使用它们对替代字母和符号以及变音符号起到了很大的作用。

我们将使用它来定义我们打算如何指示密钥的更改。换句话说,我们告诉它,当没有按下修饰键时,我们期望“级别 1”行为(通常,在“正常模式”下是标准小写字母),当我们按住按键时,我们期望“级别 2”行为Shift(通常,我们的“正常模式”中的标准大写字母),以及当我们同时按住Shift+时的“3 级”行为Mod4(对于我们的目的而言,这是一种特殊情况,我们用它来指示,当用于修改某个键时,该键现在将模式之间切换)。

{2}打开一个新文件,我们将其命名为togglekeypad.将以下代码块复制到其中,并将其保存到您的types目录下的\home\<username>\.xkb\types.注意:您可能需要将所有实例更改为Mod4“命令”/“Windows 键”按钮对应的任何修饰符(您可能需要进行试验,请参阅此网页位于修饰键下以获得指导)或您想要的任何其他修饰符。

partial default xkb_types "togglekeypad" { // Name of this type file
        type "TOGGLEKEYPAD" { // Name of this "type"
                modifiers = Shift+Mod4; // The modifiers that this type concerns itself with
                map[Shift] = level2; // Shift brings us to level 2
                map[Mod4+Shift] = level3; // Windows key plus shift brings us to level 3
                level_name[Level1] = "Base"; // Human-readable names for each level (not really used, but convenient)
                level_name[Level2] = "Shift";
                level_name[Level3] = "Transfer";
        };
};

{3}我们还必须将此文件复制到目录中/usr/share/X11/xkb/types/。这将需要 root 权限,不幸的是,这有点违背了xkb作为用户空间应用程序的目的,但setxkbmap如果不这样做,我似乎无法识别该文件。欢迎提出建议!

符号文件

接下来我们将告诉xkb每个键在以我们在类型文件中描述的每种方式修改时应该做什么。

我们会说我们想在符号文件中使用两个组。这意味着每个键都有两种不同的一般行为,我们将以某种方式在它们之间切换,这些行为是正常的打字行为和新的数字键盘模拟行为。对于每个按键,我们会说 1) 我们想要使用类型TOGGLEKEYPAD,2) 我们将定义与所有级别的两个组中的每个物理按键相关的符号(即计算机看到的内容),以及 3) 我们将定义xkb与所有级别的两个组的每个键关联的任何操作(应该执行的任何特殊操作)。这听起来相当令人困惑,但看一个例子应该会更有意义。

我们在下面粘贴的符号文件中看到的第一个键就是键<AC07>。这对应于大多数键盘上的“J”键,根据这里看到的地图(图2)。对于这个物理键,我们是说,在正常模式下:在级别 1(无修饰符,根据我们的类型文件)中,它将仅键入“j”,在级别 2(Shift修饰符)中,它将仅键入“J”。在级别 3 中,它做了一些特殊的事情:没有与级别 3 关联的符号,但有一个操作,该操作是 to LockGroup(group=2)。换句话说,将我们转到第二组,即“键盘”组。如果我们查看接下来的几行,我们会发现我们为同一个键的第 2 组定义了更多符号和操作。它表示,在级别 1(无修饰符)中,不键入任何符号,但RedirectKey(keycode=<KP1>).换句话说,注册该键就好像我们实际上刚刚按下了该<KP1>键一样,该键对应于键盘上的“1”。 (注意:我们可以再次放置 NoAction() 并使用符号KP_1,这是 key 对应的符号<KP1>,但我认为这将提供最好的兼容性)。对于级别 2,执行相同的操作,但将Shift修饰符添加到键上。最后,对于第 3 级,我们将自己锁定到第 1 组“标准”模式。

{4}打开一个新文件,我们将其命名为togglekeypad_symbols.将以下代码块复制到其中,并将其保存到您的symbols目录下的\home\<username>\.xkb\symbols.

default partial
xkb_symbols "togglekeypad" {
    name[Group1]= "Standard";
    name[Group2]= "Keypad";

    key <AC07> { // J
        type = "TOGGLEKEYPAD",
        symbols[Group1] = [ j,  J, NoSymbol],
        actions[Group1] = [NoAction(), NoAction(), LockGroup(group=2)],     
        
        symbols[Group2] = [NoSymbol, NoSymbol, NoSymbol],
        actions[Group2] = [RedirectKey(keyCode=<KP1>), RedirectKey(keyCode=<KP1>, modifiers=Shift), LockGroup(group=1)]
    };
    
    key <AC08> { // K
        type = "TOGGLEKEYPAD",
        symbols[Group1] = [ k,  K, NoSymbol],
        actions[Group1] = [NoAction(), NoAction(), LockGroup(group=2)],     
        
        symbols[Group2] = [NoSymbol, NoSymbol, NoSymbol],
        actions[Group2] = [RedirectKey(keyCode=<KP2>), RedirectKey(keyCode=<KP2>, modifiers=Shift), LockGroup(group=1)]
    };
    
    key <AC09> { // L
        type = "TOGGLEKEYPAD",
        symbols[Group1] = [ l,  L, NoSymbol],
        actions[Group1] = [NoAction(), NoAction(), LockGroup(group=2)],     
        
        symbols[Group2] = [NoSymbol, NoSymbol, NoSymbol],
        actions[Group2] = [RedirectKey(keyCode=<KP3>), RedirectKey(keyCode=<KP3>, modifiers=Shift), LockGroup(group=1)]
    };
    
    key <AD07> { // U
        type = "TOGGLEKEYPAD",
        symbols[Group1] = [ u,  U, NoSymbol],
        actions[Group1] = [NoAction(), NoAction(), LockGroup(group=2)],     
        
        symbols[Group2] = [NoSymbol, NoSymbol, NoSymbol],
        actions[Group2] = [RedirectKey(keyCode=<KP4>), RedirectKey(keyCode=<KP4>, modifiers=Shift), LockGroup(group=1)]
    };
    
    key <AD08> { // I
        type = "TOGGLEKEYPAD",
        symbols[Group1] = [ i,  I, NoSymbol],
        actions[Group1] = [NoAction(), NoAction(), LockGroup(group=2)],     
        
        symbols[Group2] = [NoSymbol, NoSymbol, NoSymbol],
        actions[Group2] = [RedirectKey(keyCode=<KP5>), RedirectKey(keyCode=<KP5>, modifiers=Shift), LockGroup(group=1)]
    };
    
    key <AD09> { // O
        type = "TOGGLEKEYPAD",
        symbols[Group1] = [ o,  O, NoSymbol],
        actions[Group1] = [NoAction(), NoAction(), LockGroup(group=2)],     
        
        symbols[Group2] = [NoSymbol, NoSymbol, NoSymbol],
        actions[Group2] = [RedirectKey(keyCode=<KP6>), RedirectKey(keyCode=<KP6>, modifiers=Shift), LockGroup(group=1)]
    };
    
    key <AE07> { // 7
        type = "TOGGLEKEYPAD",
        symbols[Group1] = [ 7,  ampersand, NoSymbol],
        actions[Group1] = [NoAction(), NoAction(), LockGroup(group=2)],     
        
        symbols[Group2] = [NoSymbol, NoSymbol, NoSymbol],
        actions[Group2] = [RedirectKey(keyCode=<KP7>), RedirectKey(keyCode=<KP7>, modifiers=Shift), LockGroup(group=1)]
    };
    
    key <AE08> { // 8
        type = "TOGGLEKEYPAD",
        symbols[Group1] = [ 8,  asterisk, NoSymbol],
        actions[Group1] = [NoAction(), NoAction(), LockGroup(group=2)],     
        
        symbols[Group2] = [NoSymbol, NoSymbol, NoSymbol],
        actions[Group2] = [RedirectKey(keyCode=<KP8>), RedirectKey(keyCode=<KP8>, modifiers=Shift), LockGroup(group=1)]
    };
    
    key <AE09> { // 9
        type = "TOGGLEKEYPAD",
        symbols[Group1] = [ 9,  parenleft, NoSymbol],
        actions[Group1] = [NoAction(), NoAction(), LockGroup(group=2)],
        
        symbols[Group2] = [NoSymbol, NoSymbol, NoSymbol],
        actions[Group2] = [RedirectKey(keyCode=<KP9>), RedirectKey(keyCode=<KP9>), LockGroup(group=1)]
    };
    
    // NumLock
    key <AE06> { // 6
        type = "TOGGLEKEYPAD",
        symbols[Group1] = [ 6,  asciicircum, NoSymbol],
        actions[Group1] = [NoAction(), NoAction(), LockGroup(group=2)],
        
        symbols[Group2] = [NoSymbol, NoSymbol, NoSymbol],
        actions[Group2] = [RedirectKey(keyCode=<NMLK>), RedirectKey(keyCode=<NMLK>), LockGroup(group=1)]
    };
    
    // Bottom Row (and zero)
    key <AB07> { // M
        type = "TOGGLEKEYPAD",
        symbols[Group1] = [ m,  M, NoSymbol],
        actions[Group1] = [NoAction(), NoAction(), LockGroup(group=2)],
        
        symbols[Group2] = [NoSymbol, NoSymbol, NoSymbol],
        actions[Group2] = [RedirectKey(keyCode=<KP0>), RedirectKey(keyCode=<KP0>, modifiers=Shift), LockGroup(group=1)]
    };
    
    key <AE10> { // 0
        type = "TOGGLEKEYPAD",
        symbols[Group1] = [ 0,  parenright, NoSymbol],
        actions[Group1] = [NoAction(), NoAction(), LockGroup(group=2)],
        
        symbols[Group2] = [NoSymbol, NoSymbol, NoSymbol],
        actions[Group2] = [RedirectKey(keyCode=<KP0>), RedirectKey(keyCode=<KP0>, modifiers=Shift), LockGroup(group=1)]
    };
    
    key <AB09> { // .
        type = "TOGGLEKEYPAD",
        symbols[Group1] = [ period,  greater, NoSymbol],
        actions[Group1] = [NoAction(), NoAction(), LockGroup(group=2)],
        
        symbols[Group2] = [NoSymbol, NoSymbol, NoSymbol],
        actions[Group2] = [RedirectKey(keyCode=<KPDL>), RedirectKey(keyCode=<KPDL>, modifiers=Shift), LockGroup(group=1)]
    };
    
    // Arithmetic Operators
    key <AE11> { // -
        type = "TOGGLEKEYPAD",
        symbols[Group1] = [ minus,  underscore, NoSymbol],
        actions[Group1] = [NoAction(), NoAction(), LockGroup(group=2)],
        
        symbols[Group2] = [NoSymbol, NoSymbol, NoSymbol],
        actions[Group2] = [RedirectKey(keyCode=<KPSU>), RedirectKey(keyCode=<KPSU>, modifiers=Shift), LockGroup(group=1)]
    };
    
    key <AE12> { // +
        type = "TOGGLEKEYPAD",
        symbols[Group1] = [ equal,  plus, NoSymbol],
        actions[Group1] = [NoAction(), NoAction(), LockGroup(group=2)],
        
        symbols[Group2] = [NoSymbol, NoSymbol, NoSymbol],
        actions[Group2] = [RedirectKey(keyCode=<KPAD>), RedirectKey(keyCode=<KPAD>, modifiers=Shift), LockGroup(group=1)]
    };
    
    key <AD12> { // [
        type = "TOGGLEKEYPAD",
        symbols[Group1] = [ bracketleft,  braceleft, NoSymbol],
        actions[Group1] = [NoAction(), NoAction(), LockGroup(group=2)],
        
        symbols[Group2] = [NoSymbol, NoSymbol, NoSymbol],
        actions[Group2] = [RedirectKey(keyCode=<KPDV>), RedirectKey(keyCode=<KPDV>, modifiers=Shift), LockGroup(group=1)]
    };
    
    key <AD12> { // ]
        type = "TOGGLEKEYPAD",
        symbols[Group1] = [ bracketright,  braceright, NoSymbol],
        actions[Group1] = [NoAction(), NoAction(), LockGroup(group=2)],
        
        symbols[Group2] = [NoSymbol, NoSymbol, NoSymbol],
        actions[Group2] = [RedirectKey(keyCode=<KPMU>), RedirectKey(keyCode=<KPMU>, modifiers=Shift), LockGroup(group=1)]
    };
};

测试我们的键盘

{5}要按原样测试键盘配置,请打开一个Terminal窗口并键入

setxkbmap -types complete+togglekeypad -print | sed -e '/xkb_symbols/s/"[[:space:]]/+togglekeypad_symbols(togglekeypad)&/' > $HOME/.xkb/keymap/customMap
xkbcomp -I$HOME/.xkb -R$HOME/.xkb keymap/customMap $DISPLAY

这将获取地图的当前设置xkb(使用setxkbmap - print),同时设置用于的类型complete+togglekeypad(文件中的所有内容/usr/share/X11/xkb/types/complete,还包括位于 的类型文件/usr/share/X11/xkb/types/togglekeypad)。然后它将把它输入到sed,这会将我们togglekeypad文件中的符号添加togglekeypad_symbols到使用的符号文件中。最后,我们用来xkbcomp编译新的键盘映射。

请注意,在我的机器上,NumLock 被假定为关闭(因为我的机器上没有小键盘),因此小键盘按键实际上会将其主要功能发送到计算机,即 Home、End、PG Up、PG Down 等要在使用模拟数字键盘时输入数字,请按住 Shift 键。我尝试了各种方法来翻转这种行为(modifers在符号文件中的级别之间交换参数,分配一个新键来模拟 NumLock 键<NMLK>并切换它),但对我来说还没有任何效果。不过值得庆幸的是,在 Blender 中进行测试时,它的表现完全符合预期,无需按住 Shift 键。

{6}如果此时出现严重错误,请不要担心,只需注销/登录(或者最坏的情况是重新启动),调试,然后重试。如果一切正常,就让它永久化吧。

使解决方案永久化

当然有一些更优雅的方法可以使我们的解决方案在会话之间持久存在,但对我来说最简单、最可靠的方法是简单地将上述命令放在文件末尾~/.bashrc。我用的是这里提出的解决方案它添加了一些错误检查,并添加了更多内容(这样我就可以看到任何错误输出)。

{7}打开文件~/.bashrc。将以下脚本添加到其末尾:

    # Setup custom keyboard remapping to emulate a number pad when "Shift+Cmd+numap_key" is pressed to initialize
if [ -d $HOME/.xkb/keymap ]; then
  setxkbmap -types complete+togglekeypad -print | \
    sed -e '/xkb_symbols/s/"[[:space:]]/+togglekeypad_symbols(togglekeypad)&/' > $HOME/.xkb/keymap/customMap 2> $HOME/.xkb/log/sedErrors
  xkbcomp -w0 -I$HOME/.xkb -R$HOME/.xkb keymap/customMap $DISPLAY > $HOME/.xkb/log/outputOfCommand 2>&1
fi

{8}重新启动后,数字键盘模拟现在应该永久生效!

结论

虽然解释很长,但方法本身相对较短。缺点是 Blender 需要锁定方法才能正常工作,而我更喜欢使用保持方法,而且xkb由于某种原因它需要 root 访问权限才能识别我们的自定义类型文件。然而,总的来说,这对我来说似乎效果很好。如果您有任何疑问或建议,请随时在下方留言!

相关内容