Linux 永久更改鍵盤設定

前言

最近跟同事借了一把Filco 60%茶軸來玩,他有特別的雙fn設計,同時按 fn和; 就等於backspace,fn和s/d/e/f 就變成方向鍵,手腕都不用動,爽爽的。

可是一把要四千五,而且s/d/e/f跟Vim的h/j/k/l不一樣,用起來還是有點不太習慣。

於是,經過幾天的hacking,我終於可以直接改變Linux內建的鍵盤設定,讓筆電鍵盤也可以像Filco的fn一樣。

第一篇Medium,就這麼誕生啦!!

一、Xmodmap

Xmodmap是比較舊、比較low-level的鍵盤設定工具,不過設定相對簡單,容易做一些基本的按鍵置換(比如換Capslock跟Control的位置),因此在各大網站也常常被提到。

首先可以利用 xev 指令看每個按鍵的keycode:

$ xev
KeyPress event, serial 36, synthetic NO, window 0x3e00001,
root 0x86, subw 0x0, time 890946390, (803,237), root:(804,294),
state 0x2, keycode 66 (keysym 0xffe3, Control_L), same_screen YES,
XLookupString gives 0 bytes:
XmbLookupString gives 0 bytes:
XFilterEvent returns: False
KeyPress event, serial 36, synthetic NO, window 0x3e00001,
root 0x86, subw 0x0, time 891083183, (793,9), root:(794,66),
state 0x0, keycode 37 (keysym 0xffe5, Caps_Lock), same_screen YES,
XLookupString gives 0 bytes:
XmbLookupString gives 0 bytes:
XFilterEvent returns: False

然後用簡單的指令:

xmodmap "keycode 37 = Caps_Lock"
xmodmap "keycode 66 =
Control_L"

就可以交換Capslock跟左Control了。每次登入都要要重新輸入有點麻煩,可以把設定放在~/.Xmodmap裡面:

remove Lock = Caps_Lock
remove Control = Control_L
keysym Control_L = Caps_Lock
keysym Caps_Lock = Control_L
add Lock = Caps_Lock
add Control = Control_L

然後把xmodmap ~/.Xmodmap放到開機會執行的設定檔(~/.bashrc~.zshrc等等),登入時就會自動執行。想復原的話,可以用setxkbmap -option來重置。

這是我的~/.Xmodmap設定:

 remove Lock = Caps_Lock
remove Control = Control_R
keysym Control_R = Caps_Lock
keysym Caps_Lock = Control_R
add Lock = Caps_Lock
add Control = Control_R

keycode 66 = Mode_switch
keysym h = h H Left
keysym l = l L Right
keysym k = k K Up
keysym j = j J Down
keysym u = u U Prior
keysym i = i I Home
keysym o = o O End
keysym p = p P Next
keysym d = d D Return
keysym f = f F Return
keysym semicolon = semicolon colon BackSpace

以上設定把沒有用的Capslock移到更沒用的右Control,然後把原本的Capslock改成Mode_switch(有點類似fn,有時候也叫AltGr)所以同時按Capslock+h/l/k/j/u/i/o/p/d/f/;就會變成上面keysym第三格的Left/Right/Up/Down/PageUp/Home/End/PageDown/Enter/BackSpace,這下連按Enter都不用動手腕了,比Filco預設的還方便!

(你可能注意到了,keysym定義第一個是正常使用的按鍵,第二個就是按Shift的時候會出現的字,這是Level的概念,之後會解釋。你也可以把Shift+a定義成B,或任何輸出)

使用Xmodmap的優點是簡單,不過缺點是如果系統休眠或切換layout(比如說換到新酷音),就得再執行一次xmodmap ~/.Xmodmap,非常困擾,也很難解決。 有人寫了一個python script自動幫忙執行這個指令,可以參考看看。

二、X keyboard extension (XKB)

一直沒辦法解決換輸入法時Xmodmap會被重設的問題,讓我困擾了許久,最後在這裡找到了以XKB的根本解法。

XKB是現在Linux使用的鍵盤設定,觀念規則相當複雜,教學也不是很多,可以參考Arc Linux的Wiki,或 Chunlin-Li的中文介紹。XKB的設定檔都在/usr/share/X11/xkb底下:

$ ls /usr/share/X11/xkb
compat geometry keycodes rules symbols types

其中

  • keycodes是定義每個按鍵的代號,比如說<AD01>通常是鍵盤上Q的位置
  • symbols是定義每個按鍵實際按下去的動作,比如說按下鍵盤上的Q,通常會打出q
  • rules則是一些系統存取解釋檔,比如說設定鍵盤layout的時候可以選取各種語言等等

在這邊我們主要要改的是symbols。在symbols下面可以看到各種語言的設定,他們彼此可能具有繼承的關係,以一般常見的英文鍵盤`us`為例,basic定義了基本的鍵盤(繼承最基本的定義,pc):

default  partial alphanumeric_keys modifier_keys
xkb_symbols "basic" {

name[Group1]= "English (US)";
key <TLDE> { [ grave, asciitilde ] };
key <AE01> { [ 1, exclam ] };
key <AE02> { [ 2, at ] };
key <AE03> { [ 3, numbersign ] };
key <AE04> { [ 4, dollar ] };
key <AE05> { [ 5, percent ] };
key <AE06> { [ 6, asciicircum ] };
key <AE07> { [ 7, ampersand ] };
key <AE08> { [ 8, asterisk ] };
key <AE09> { [ 9, parenleft ] };
key <AE10> { [ 0, parenright ] };
key <AE11> { [ minus, underscore ] };
key <AE12> { [ equal, plus ] };
(...)

但由於每個地區的英文鍵盤不盡相同,對每個鍵盤都會有些微調的設定,比如說同樣在us檔裡面的euro,就是繼承了us(basic)

partial alphanumeric_keys
xkb_symbols "euro" {

include "us(basic)"
name[Group1]= "English (US, with euro on 5)";

include "eurosign(5)"

include "level3(ralt_switch)"
};
各種英文鍵盤layout

可以利用setxkbmap -print -verbose 10來看目前的設定。

而我們也可以自己定義新的XKB,甚至可以改icon,這裡有詳細的教學。

自己新增的XY鍵盤

不過我自己繼承us(basic)出了一些問題,後來就直接改us(basic)的設定。這並不是一個最好的辦法,在電腦更新的時候可能會被洗掉,若要這麼做建議先備份到GitHub。

以下是我對/usr/share/X11/xkb/symbols/us的更改(須以sudo權限):

default  partial alphanumeric_keys modifier_keys
xkb_symbols "basic" {

name[Group1]= "English (US)";

key.type = "ONE_LEVEL";
key <CAPS> { [ ISO_Level3_Shift ] };
modifier_map Mod5 { ISO_Level3_Shift };

key <RCTL> { [ Caps_Lock ] };

key.type = "THREE_LEVEL";
(...)
key <AD08> { [ i, I, Home ] };
key <AD09> { [ o, O, End ] };
(...)
key <AC03> { [ d, D, Return ] };
key <AC04> { [ f, F, Return ] };
key <AC05> { [ g, G ] };
key <AC06> { [ h, H, Left ] };
key <AC07> { [ j, J, Down ] };
key <AC08> { [ k, K, Up ] };
key <AC09> { [ l, L, Right ] };
key <AC10> { [ semicolon, colon, BackSpace ] };
(...)
key <AB07> { [ m, M, Prior ] };
key <AB08> { [ comma, less, Next ] };
(...)
};
(...)

基本上就是跟先前Xmodmap一樣,把Capsloack+h/j/k/l…改成各種快捷鍵。其中:

  • key.type = “THREE_LEVEL”;定義了下面的按鍵都有三種模式(一般/按shift/按Mode_switch)
  • ISO_Level3_Shift相當於先前提到的Mode_switch,一種modifier,是level3(Shift就是ISO_Level2_Shift,也可以定義更高的level)
  • Modifiers不會受到Shfit改變行為,如ctrl/alt/enter/backspace等等
  • 在[]內的是一個group,你也可以設定多個group,然後設定按鍵在這些group之間切換

改完之後執行sudo dpkg-reconfigure xkb-data清空XKB的cache,再用setxkbmap us設定只用US layout,切換輸入法應該就會生效了。因為是直接更改default的設定,有問題可能會讓你的鍵盤不能正常運作,改的時候千萬要小心。若沒把握,可以先這篇的教學,設定額外的鍵盤,不會影響到US的設定。

使用XKB的好處是可以永久改變鍵盤設定,不受切換輸入法(在新酷音也可以用!)及休眠的影響,並且可以做複雜的改變(定義多種Level及Group等等);缺點是規則繁多,要花時間理解,且不小心可能會讓鍵盤不能正常運作。

三、XCAPE

XCAPE是一個有趣的套件,可以讓上面提到的Modifiers有一般按鍵的功能。

安裝完後執行xcape -e ‘ISO_Level3_Shift=Escape’,可以讓被定義成ISO_Level3_Shift的鍵(就是我在XKB US設訂的Capslock)有ESC的功能,這樣一來在Vim insert mode的時候只要單按Capslock就可以跳出insert mode了!!而原本設定的Capsloack+h/j/k/l…的改變也不受影響,切換輸入法也一樣有用。

總結

以往不太注意自己的打字習慣,這一年來常常因為coding手掌肌腱發炎,最近才開始慢慢調整。經過漫長的爬文獻&try-and-error,終於把鍵盤設定好啦!有種洗出+7鍵盤的感覺XD,總算可以開始認真寫code了~