Keyboard-Layout

您如何模擬數字鍵盤/小鍵盤以與設計軟體(例如 Blender)一起使用?

  • September 29, 2020

問題

在使用某些軟體(例如Blender)時,重要的是能夠使用數字鍵盤,以便使用者可以做一些事情,例如在設計空間中定位自己,但許多筆記型電腦沒有配備物理數字鍵盤。由於正常數字條輸入(鍵盤頂部的 1-9)實際上對電腦表示不同的“符號”,因此在這些類型的軟體中通常具有完全不同的功能,這使情況變得更加複雜。

嘗試的解決方案

由於許多筆記型電腦沒有配備小鍵盤,因此許多平台上的常見解決方案是模擬小鍵盤,例如按住一個鍵同時使用鍵盤上的其他鍵(例如 jkluio789 代表 123456789)。許多筆記型電腦在 BIOS 級別實現此功能(例如使用 Fn 鍵)。但是,如果沒有這樣的低級實現,實現這種仿真是非常困難的。

一些解決方案線上存在,但它們通常不足以與設計軟體一起使用(因為它們沒有實現正確的符號,並且還需要使用修飾鍵),或者它們沒有深入解釋。大多數解決方案都專注於使用xkb,這是一個複雜且眾所周知難以開始使用的架構。

良好解決方案的要求

這個問題的一個很好的解決方案是一個模擬鍵盤,圖形軟體將接受它作為真正的鍵盤輸入,並且易於使用。另一個限制是即使在被告知清除修飾符時,Blender 也會檢測到修飾鍵(例如Shift、、(“Command”、“Windows 鍵”等)、 )的使用,因此將解釋“鍵盤仿真同時持有修飾鍵”解決方案作為完全不同的輸入(即 [ + ] 而不是 just )。因此,理想的解決方案實際上將涉及鎖定機制(例如 Caps Lock 大寫)而不是保持機制(例如 Shift 大寫),這樣不會意外地將修飾符傳遞給軟體。Alt``Super``Hyper``xkb``Numpad1``Alt``Numpad1

快速開始

如果您不關心任何解釋(我知道我可能會囉嗦),只需按照某些段落開頭的**{大括號中的粗體數字}即可。**按順序執行這些步驟中的每一個,您可能會在幾分鐘內完成此操作。請注意,本指南假定您具備一定的 Unix 能力(能夠創建目錄、創建文件、sudo提升到 root 訪問權限等)。另請注意,僅在指示時才需要 root 訪問權限sudo,因此除非被告知,否則您無需使用。

解決方案的一般描述

我們將使用 xkb 向 Linux 添加一個“鎖定”(如 Caps lock)數字鍵盤模擬。我希望我的鍵“jkluio789”代表數字“123456789”的小鍵盤表示,以及其他一些包含(“m”,->“0”,“-=

$$ $$"->$$ numpad $$"-+*", “.”->$$ numpad $$“。”)。我將使用 [ Shift+ Mod4+ [key]] 的組合鍵切換此“數字鍵盤模式”,其中Mod4是我的作業系統鍵的修飾符程式碼(也稱為“命令”或“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”的行為(通常,在我們的“正常模式”下是標準的小寫字母),當我們按住Shift鍵時,我們期望“級別 2”的行為(通常,我們的“正常模式”中的標準大寫字母)和“3級”行為u當我們同時持有Shift+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>鍵。根據此處看到的地圖(圖 2),這對應於大多數鍵盤上的“J”鍵。對於這個物理鍵,我們說的是,在普通模式下:在級別 1(根據我們的類型文件,沒有修飾符)它只會輸入“j”,而在級別 2(Shift修飾符)中它只會輸入“J”。在第 3 級,它做了一些特別的事情:沒有與第 3 級相關的符號,但有一個動作,那個動作是 to LockGroup(group=2)。換句話說,將我們切換到我們的第二組,我們的“鍵盤”組。如果我們查看接下來的幾行,我們會看到我們為第 2 組為同一個鍵定義了更多的符號和動作。它說,在 1 級(無修飾符)中,沒有符號,但是RedirectKey(keycode=<KP1>). 換句話說,註冊這個鍵,就好像我們實際上只是按下了<KP1>鍵一樣,它對應於鍵盤上的“1”。(注意:我們可以再次放入 NoAction() 並使用符號KP_1,這是鍵<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 訪問權限才能辨識我們的自定義類型文件。但是,總的來說,這對我來說似乎很有效。如果您有任何問題或建議,請隨時在下方留言!

引用自:https://unix.stackexchange.com/questions/580280