最近常用Putty訪問Linux,在上面用Emacs修改代碼、運行shell、進行SQL交互,最大的問題是很多組合鍵(比如我常用的Shift+Up/Down/Left/Right, Ctrl+F1..F12, Ctrl+Home/End)不能使用,春節前兩天有些時間,研究了一下這個問題,算是基本找到了一些解決辦法。
問題分析
首先需要了解Xterm control sequences這個概念,這個鏈接中的說明很晦澀,我簡單地說明一下:對於有對應 ASCII字符的組合健,就發送對應的ASCII字符,否則用一個特殊序列來表示,比 如 F1 就用 ^[OP
(也就是先按Ctrl+[, 然后按O,然后再按P),或者用 ^[[11~
來表示, C-F7 就用 ^[[18;5~
表示 。這個序列一般都是以 ESC(也即ASCII碼中的27,對應實際按鍵 ESC 或者 Ctrl+[ ,常寫為 \e
或者 ^[
)開始,所以也常常叫做Escape sequences(除了表達按鍵之外,它還用來表達終端顏色控制)。
當在終端里運行應用程序時,按下一個組合鍵之后的事情是這樣:
- 終端模擬器將其翻譯為對應的Xterm control sequences,發送給對端主機;
- 對端主機根據
TERM
在terminfo中查找翻譯表,翻譯為對應的按鍵信息,傳遞給應用程序(如果某些組合鍵在翻譯表中找不到,則透傳) - 應用程序對組合健做出響應。
所以可能存在的問題是:
- 終端模擬器不能將某些組合鍵翻譯為Xterm control sequences
- 終端模擬器與terminfo中的翻譯表不一致
對於第一個問題
可以用AutoHotKey攔截組合鍵,自行翻譯到control sequences發送出去:
#if WinActive("ahk_class PuTTY") or WinActive("ahk_class mintty") ;;** Ctrl+Fn ^F1::SendInput {ESC}[1;5P ^F2::SendInput {ESC}[1;5Q ^F3::SendInput {ESC}[1;5R ^F4::SendInput {ESC}[1;5S ^F5::SendInput {Esc}[15;5~ ^F6::SendInput {Esc}[17;5~
(完整的代碼見這里: bamanzi / dotemacs-elite / source / term / putty-ctlseqs.ahk — Bitbucket)
備注一下,我的辦公用機是Windows,我用的終端模擬器是putty及其變種 (包括mintty, putty-nd以及MobaXterm)。至於Linux桌面,GNOME Terminal和XFCE Terminal兼容vt102和vt220的組合鍵,Ctrl+Up, Alt+F1這些大都是支持的(但有少數不支持,但我目前不知道什么工具可以完成類似AutoHotKey這樣的功能)
對於第二個問題
對於Emacs而言,我們並不需要直接去解決第二個問題,因為Emacs可以自己將control sequences翻譯為其它按鍵:
(define-key input-decode-map "\eO5P" [C-f1]) (define-key input-decode-map "\eO5Q" [C-f2]) (define-key input-decode-map "\eO5R" [C-f3]) (define-key input-decode-map "\eO5S" [C-f4]) (define-key input-decode-map "\e[15;5~" [C-f5]) (define-key input-decode-map "\e[17;5~" [C-f6])
這里有一個比較完整的包,包含了很多control sequences: http://www.dur.ac.uk/p.j.heslin/Software/Emacs/Download/xterm-extras.el )
補充說明
- 對於同一個鍵,可能有多種control sequences,比如F1有
^[11~
,^[[1P和^[OP 這三種序列,但每種終端模擬器只發送其中一種。上面的xterm-extra.el 對光標鍵提供了CSI和SS3序列的映射,這意味着在多種終端上按這些鍵都可以正確翻譯; 但對於F1..F4只提供了CSI的序列映射,沒有提供SS3序列映射(而Putty/Mintty對於F1..F4恰好使用的是CSI序列)。不過反過來比較好的是,不會一個序列對應多種按鍵。
- 獲取你的終端上某個鍵的control sequences的簡單方法是用cat命令(不過對於少數特殊鍵和application keypad不好使)
- 上面的xterm-extra.el里面對keypad序列似乎不是標准的,我沒有查到^[z開頭的序列,似乎是作者利用了一個尚未使用的序列。不過SS3序列中是有keypad序列的(vim wiki中有一篇相關的貼士: PuTTY numeric keypad mappings - Vim Tips Wiki;
- Putty本身對於F1, Shift+F1, Alt+F1, Alt+Shift+F1, 支持挺好,但不支持帶Ctrl修飾鍵的(比如Ctrl+F1, Ctrl+Shift+F1, Ctrl+Alt+F1),如果是Windows平台,可以用AutoHotKey來解決;
- 對於Ctrl+標點這個列表,部分(比如Ctrl+*, Ctrl+.等)是有control sequences定義的,還有一些(比如Ctrl+!, Ctrl+#, Ctrl+; )則沒有正式的control sequences。詳細說明可以參考我的配置文件里面的說明: init.d / 80-ports.el。如果要使用,也只能用AutoHotKey來解決
(這篇文章有個完整的版本,包含了更多細節,我放在了bitbucket里面,如果有興趣,可以前往閱讀)