改用 alacritty

原先習慣用 terminator, 為何不用 gnome-terminal 的原因甚至已經忘了。某天看到 ghostty 宣傳說顯示速度很快,看了一下測試報告都是跟 alacritty 比較,表示應該也很快,就決定都玩一下。結果用了 alacritty 覺得很「斯巴達」,尤其那個 vi mode, 正合胃口。ghostty 一堆功能似乎也不需要,就放著了,先換成 alacritty.

過程還蠻順的,主要是以下幾個設定:

TERM 是 alacritty, 先要讓 .bashrc 認得。首先是彩色的提示詞:

1
2
3
4
# set a fancy prompt (non-color, unless we know we "want" color)
case "$TERM" in
xterm-color|*-256color|alacritty*) color_prompt=yes;;
esac

以及後面改 window title 的部份:

1
2
3
4
5
# If this is an xterm set the title to user@host:dir
case "$TERM" in
xterm*|rxvt*|alacritty*)
PS1="\[\e]0;${debian_chroot:+($debian_chroot)}\u@\h: \w\a\]$PS1"
;;

這邊有個麻煩,因為其他機器上的 .bashrc 沒改,所以連過去以後又變成不認得,乾脆在 ~/.ssh/config 裡面加一行:

1
SetEnv TERM=xterm-256color

這樣連出去時就會用很平常的 xterm-256color. 然後是設定檔 ~/.config/alacritty/alacritty.toml

1
2
3
4
5
6
7
8
9
10
[general]
import = [
"~/code/alacritty-theme/themes/solarized_dark.toml",
]
[font]
size = 10
[keyboard]
bindings = [
{ key = "P", mods = "Control|Shift", command = { program = "alacritty.sh", args = [ "ct", ]}}
]

這邊就是先把 alacritty-theme 抓到 ~/code/ 然後預設為慣用的 solarized_dark. [keyboard] 則是多設定一快速鍵可以切換 theme. 針對這寫了一個小 script, alacritty.sh:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#!/bin/bash

ALACRITTY_THEME="/tmp/alacritty-theme"
THEME=solarized_dark

case "$1" in
change-theme|ct)
case "$(cat "$ALACRITTY_THEME")" in
solarized_dark) THEME=solarized_light;;
solarized_light) THEME=google;;
esac
alacritty msg config "$(cat ~/code/alacritty-theme/themes/${THEME}.toml)"
notify-send "$THEME"
echo "$THEME" | tee "$ALACRITTY_THEME"
exit
;;
esac

if pgrep -u $(id -u) 'alacritty$' &> /dev/null; then
exec alacritty msg create-window "$@"
else
echo "$THEME" > "$ALACRITTY_THEME"
exec alacritty "$@"
fi

這 script 有多重功能,除了加 change-themect 可以在 solarized_dark, solarized_light, google 三個 theme 循環切換之外,一般呼叫時也會偵測是否已經在執行,如果有的話,開新視窗就好,不用多跑一份。

最後一個設定是針對 Emacs 的。以前寫過 Emacs 彩色終端,這邊就是要加上 alacritty-direct. 一樣用 wrapper script emacs.sh 達成。功能除了設定 TERM, 還會看是否已經在執行(因為我都用 server-mode),如果有,就直接跑 emacsclient.

1
2
3
4
5
6
7
8
9
10
11
#!/bin/bash

if pgrep -u $(id -u) 'emacs$' &> /dev/null; then
case x"$TERM" in
xalacritty*) export TERM=alacritty-direct;;
xxterm-256color) export TERM=xterm-direct;;
esac
exec emacsclient -t "$@"
else
exec /usr/bin/emacs "$@"
fi

用起來真的蠻有感的,顯示什麼都是刷一下就結束。還不錯。

Grammar Checker for Emacs

看到 Harper 消息,想說來裝裝看,照討論區中方式,卻碰到跟 eglot 不合的情況,看起來問題出在以下這段:

1
2
3
4
5
6
[jsonrpc] e[01:26:56.418] <-- workspace/configuration[0] {"jsonrpc":"2.0","method":"workspace/configuration","params":{"items":[{}]},"id":0}
[jsonrpc] e[01:26:56.420] --> workspace/configuration[0] {"jsonrpc":"2.0","id":0,"result":[null]}
[jsonrpc] e[01:26:56.420] <-- workspace/configuration[1] {"jsonrpc":"2.0","method":"workspace/configuration","params":{"items":[{}]},"id":1}
[jsonrpc] e[01:26:56.422] --> workspace/configuration[1] {"jsonrpc":"2.0","id":1,"result":[null]}
[stderr] 2025-03-02T17:26:56.420461Z ERROR harper_ls::backend: Settings must be an object.
[stderr] 2025-03-02T17:26:56.422463Z ERROR harper_ls::backend: Settings must be an object.

此文寫下時 harper 最新版本為 v0.23.0, 後退到 v0.22.0 還是不動就放棄了。Grammarly 似乎很紅,但要花錢才能用 API 不然只能網頁使用,放棄。改試老牌的 LanguageTool, 比較好用的對應 language server 是 ltex-ls, 貌似沒什麼在維護,最新版 16.0.0 會有無回應情況,退到 15.2.0 就會動。相關的 emacs 設定如下:

1
2
3
4
5
6
7
(use-package eglot
:hook
(text-mode . eglot-ensure)
:config
(add-to-list 'eglot-server-programs
;; '(text-mode . ("harper-ls" "-s"))))
'(text-mode . ("ltex-ls"))))

最大收穫是 eglot 不錯,而且已經 merge 進 emacs 成為發行的一部分了。不過大致看了一下,lsp-mode 目前還是比較好用的,也許之後再觀察一下吧。

翻找過程中意外發現一位工程師,似乎專門在寫沒什麼必要的 emacs package, 大部分都沒進 gnu or melpa, 像是 eglot 直接可用的情況,他就會去寫個 eglot-ltex, 大概是想賺 contribution 吧。蠻有意思的。

另個有趣的發現是 vale, 很認真在開發,但沒有檢查文法功能,只能查拼字跟 writing style, 因此發現還有 Google developer documentation style guide 這種東西。照著裡面的規則、再加上用 LLM 潤飾的話,就可以寫出煞有介事的文件。

Enable ThinkPad fingerprint sensor 138a:0097 for Ubuntu 20.04

稍微搜尋一下以後,發現 validity-sensors-tools 可以用,裡面指向的 3v1n0/libfprint 有提供 ppa, 但裝起來不動。看一下 log 會發現類似這樣的訊息:

1
Sep 24 20:31:30 ThinkPad-X1.localdomain fprintd[8281]: Expected len: 84, but got 108

找了一下發現這個 patch, 先用 apt-get source 抓 ppa 的 source code 下來,接 著用apt-get build-dep 抓編譯所需的其他 packages. 這邊碰到一個問題,由於上游已 經有比較新的版本了,會變成不需要 libfprint-2-tod-vfs0090, 這只要把相關 packages 版本都先 hold 在 1:1.90.1+tod1-0ubuntu4+vfs0090~f2 就可以解決。

成功抓下來之後,把 patch 打上去,debuild -i -us -uc -b, build 出來裝起來就會動 了。不過 libpam-fprintd 的預設是如果在 pam-auth-update 裡面勾起來,是可以用 來登入,這不是我想要的行為,所以就不勾,直接改 /etc/pam.d/sudo:

1
2
3
auth       sufficient pam_fprintd.so max_tries=1 timeout=5

@include common-auth

這樣 sudo 的時候比較方便,不用打落落長的密碼。

以記憶體為暫時檔案系統

用記憶體作為暫時檔案系統是個好主意,除了速度快,也可以減少寫入 SSD 次數。但使用有點麻煩,又一直懶得動手寫 script 就拖著了 (Story of my life)。前陣子看了這篇SSD Optimization 裡面介紹的幾個工具都覺得不合用,終於決定寫一個,其實也才幾行…

適用場景:打算開始在某目錄下寫程式、頻繁編譯,或類似情況。

使用方式: tmpoverlay.sh <your project dir>

特點:

  1. 採用 overlay filesystem, copy-on-write, 只有改了才會紀錄,不用「整個 copy 出來用完再蓋回去」,快、也避免 buffer / tmpfs 的重複。
  2. trap EXIT, 理論上 Ctrl-C 結束或收到 SIGTERM 都會複製回去,不會掉資料。所以就算忘記了直接登出關機應該也 OK. (沒測過)

缺點:需要 sudo NOPASSWD

Code 如下:

gnome-keyring-daemon 搭配 openssh

又是一個網路上搜尋到的資訊都不太對的問題。只有提 issue 的這邊寫得好些。

一開始是看著一堆沒加密的 ssh private keys 覺得不太舒服,想把他們通通加密起來。可 是重開機後第一次用要打密碼又很麻煩,就想搭配 gnome-keyring-daemon 使用,自動記 住密碼。本來以為是由於我的桌面環境不是標準的 Ubuntu 桌面才不會動,後來發現用標準 的登入也不行 (20.04 LTS)。

標準環境下

假設用標準環境,/usr/share/xsessions/ubuntu.desktop 長這樣:

1
2
3
4
5
6
7
8
9
[Desktop Entry]
Name=Ubuntu
Comment=This session logs you into Ubuntu
Exec=env GNOME_SHELL_SESSION_MODE=ubuntu /usr/bin/gnome-session --systemd --session=ubuntu
TryExec=/usr/bin/gnome-shell
Type=Application
DesktopNames=ubuntu:GNOME
X-GDM-SessionRegisters=true
X-Ubuntu-Gettext-Domain=gnome-session-3.0

看起來一部份是用 systemd 去跑 session. 看一下 /usr/lib/systemd/ 的確是這樣。 首先 user-preset/90-systemd.preset 裡面有

1
2
3
4
5
# Passive targets: always off by default, since they should only be pulled in
# by dependent units.

disable graphical-session-pre.target
disable graphical-session.target

圖形界面的東西預設是關的,在 user/gnome-session.target 裡面才又開起來:

1
BindsTo=graphical-session.target

接下來是 user/gnome-keyring-ssh.service 重點在

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[Unit]
Description=Start gnome-keyring as SSH agent
Before=graphical-session-pre.target ssh-agent.service
PartOf=graphical-session-pre.target

<snipped>

ExecStart=/bin/sh -ec 'if [ -z "${SSH_AUTH_SOCK}" ] && \
! grep -s -q X-GNOME-Autostart-enabled=false ~/.config/autostart/gnome-keyring-ssh.desktop /etc/xdg/autostart/gnome-keyring-ssh.desktop; then \
eval $$(/usr/bin/gnome-keyring-daemon --start --components ssh); \
dbus-update-activation-environment --verbose --systemd SSH_AUTH_SOCK=$$SSH_AUTH_SOCK SSH_AGENT_LAUNCHER=gnome-keyring; \
initctl set-env --global SSH_AUTH_SOCK=$$SSH_AUTH_SOCK || true; \
fi'
ExecStopPost=/bin/sh -ec 'if [ "${SSH_AGENT_LAUNCHER}" = gnome-keyring ]; then \
dbus-update-activation-environment --systemd SSH_AUTH_SOCK=; \
initctl unset-env --global SSH_AUTH_SOCK || true; \
fi'

所以要在圖形界面下,檢查 SSH_AUTH_SOCK 沒設定且 autostart 有開才會跑。於是把系 統的 gnome-keyring-ssh.desktop copy 到自家目錄 ~/.config/autostart 底下,並 且把裡面原本的 X-GNOME-Autostart-enabled=false 改為 true, 結果還是一樣不跑,可 見在此之前 ssh-agent 已經跑起來了。到 /etc/X11/Xsession.d 底下看到 90x11-common_ssh-agent 這個會先跑,導致後面進 gnome-session 時 SSH_AUTH_SOCK 已經設定了。這個要改 /etc/X11/Xsession.options 把裡面 use-ssh-agent comment 掉,之後就運作正常。

awesome desktop manager 環境下

沒有 systemd, 自己執行就行了。但內建的 awesome.desktop 就只是直接執行 awesome 而已,沒辦法先跑 gnome-keyring-daemon, 直接新增一個 desktop 檔自己來:

1
2
3
4
$ cat /usr/share/xsessions/xsession.desktop
[Desktop Entry]
Name=Xsession
Exec=/etc/X11/Xsession

注意 Xsession.options 裡面要有 allow-user-xsession. 接下來 ~/.xsession 簡 單寫就好:

1
2
3
4
5
6
7
8
9
#!/bin/bash

# /etc/X11/Xsession.d/55awesome-javaworkaround
_JAVA_AWT_WM_NONREPARENTING=1
export _JAVA_AWT_WM_NONREPARENTING

eval `gnome-keyring-daemon -s`
export SSH_AUTH_SOCK
exec awesome

搭配使用效果

首先把沒加密的 private key 都用 ssh-keygen -p 加密起來,然後實際使用它來連線, 例如 ssh -i .ssh/id_ed25519 之類的,這時 ssh 就會按照 SSH_AUTH_SOCK 裡面的設 定去問 keyring-daemon, daemon 就會跳一個圖形界面出來問密碼:

輸入密碼

把下面那個自動解鎖的選項打勾,密碼就會記到 keyring 裡面了。執行 seahorse 可以 看到目前列管的 ssh keypairs, 注意要在 ~/.ssh 目錄下 xxxxxx.pub 成對的 才會出現。

KBt RE:68 鍵版本

KBt RE:68

會買這隻鍵盤基本是個實驗。在 75% 鍵盤 提過,奕之華先前以 KBtalKing 為品牌推出的 RACE 鍵盤,取代 FILCO Majestouch TKL 作為主力已使用多年。這次 KBt 復出,本就有意願再次支持。最主要的猶豫點是,第一波僅有 66 鍵跟 68 鍵版本。就我的使用習慣而言:

  1. F1 ~ F12 使用頻率沒那麼頻繁,偶然要使用時加按 Fn 尚可接受。
  2. 保留了 Home, End, PgUp, PgDn 以及方向鍵,都是最常使用的。
  3. 剩下問題就是 ESC / ` 鍵的取捨,以及 PrtSc/SysRq 問題。

PrtSc 為何是問題?因為這個鍵對 Linux 使用者非常重要,是用來 緊急救援 的,例如 Alt-SysRq-S 之類。雖然 SysRqFn-P 可以按出來,問題是後面的字母例如 0 ~ 9 還有 S/I/J/K/L 之類,搭配 Fn 都有定義了。比如說 Fn-S 的媒體鍵,而 Fn-[0 ~ 9] 就是 F1 ~ F10 的功能鍵。所以用 Alt-Fn-P-S 這樣順序按下的話,實際上效果是 Alt-SysRq-,基本是按不出來的。這點還算有解,只要把大部分 Fn 搭配字母鍵的功能取消或移走就可以,至於 Alt-SysRq-0 ~ 9 其實沒那麼常用,乾脆捨棄,反正都是用筆電,還有原本鍵盤備援。

接下來談到 ESC / `,這是個大問題,ESC / ` / ~ 這三個鍵都相對常按,ESC 不用說,~ 在 Linux 上極常用也不用說,` 現在也越來越常出現在各語言中。奕之華在 「KBtalKing RE:鍵盤的FN複合鍵論述:」 這篇提出的解法是以 ESC 為主鍵,Shift-ESC 可出 ~,而 Fn-ESC`. 如此只有 ` 需要多按 Fn 其他都沒影響,是個能接受的解決方案,就立馬下訂了。

不幸的是,拿到貨以後,Shift-ESC 這組合試不出來,去信詢問後,得到回答是發現有些問題所以取消了。之後幾天一直嘗試各種組合,例如 ESC` 交換,這完全不行,ESC 實在太常用。試著把 ` 移到其他位置例如右 Ctrl 之類的,也不行。這是個人的使用習慣,即便把 CapLock 改為 Ctrl 了,原先左右 Ctrl 還是很常按,使用情境是以掌緣按壓,會看當下情境採用三個 Ctrl 之中最省力的來用。這是被 Emacs 害的。

試來試去沒有好辦法,只好出大招,鍵盤不能改,就改系統環境吧。

/usr/share/X11/xkb/symbols/local 裡面本來就被我塞了幾個特殊設定,現在加上:

1
2
3
xkb_symbols "esc" {
replace key <ESC> { [ Escape, asciitilde ] };
};

如此就把這台電腦設定為,不管任何鍵盤按 Shift-ESC 都會出 ~。乍看是解了,但這組合剛好跟 Chrome 叫出 Task Manager 的熱鍵衝到,所以在 Chrome 裡面要打 ~ 的話,還是要按 Shift-Fn-ESC,還算能接受的妥協。目前度過兩個紮實工作天,打感比先前的 RACE 好上很多,就當成新的主力鍵盤了。

如果能像 HHKB 一樣,把 \ 改成 Backspace,然後把原先的 Backspace 拆成 \`,就可以說是我心目中理想的鍵盤 layout 了,但似乎都沒人出這樣的。

HHKB 改造版

Chrome headless 模式下 DevToolsActivePort file doesn't exist 問題

同事寫自動測試碰到這問題,但網路上能找到的答案都跟真實原因不同,所以記在這裡。先在 Linux 環境下以使用者 A 的身份執行

1
google-chrome --disable-gpu --headless "https://google.com/"

然後以使用者 B 執行同樣命令,就會出現類似這樣的錯誤:

1
[0514/015330.359583:ERROR:filesystem_posix.cc(63)] mkdir /tmp/Crashpad/new: Permission denied (13)

而如果是在 Selenium 裡面執行的話,最後出現的問題就是 DevToolsActivePort 檔案不存在,因為沒權限。

要解決也蠻簡單:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import os.path

from selenium import webdriver


def main():
options = webdriver.ChromeOptions()
options.add_argument("disable-gpu")
options.add_argument("headless")
options.add_argument(f"crash-dumps-dir={os.path.expanduser('~/tmp/Crashpad')}")
driver = webdriver.Chrome(options=options)
driver.get("https://google.com/")
input("press enter")
driver.quit()


if __name__ == "__main__":
main()

在 home directory 底下開個 tmp/Crashpad 專門用來放就可以了。

Emacs 彩色終端

Emacs 使用者常跑 server-mode 來快速開檔。個人習慣是用 emacsclient -n 把檔案丟去 Emacs 視窗且保持終端機可用,偶爾要在終端機裡面直接編輯的話就會用 emacsclient -t. 不過最近從 Solarized-light 換成 Solarized-dark 之後,之前懶得解的老問題又出現:這主題在終端機下選的背景是 blue, 顯示出來直接是亮藍色,就是 Windows 當機那顏色,實在刺眼。

Blue Screen of Death

本來想的改法是直接都改成終端機標準色,這樣終端機換配色的時候就會跟著換。但後來在 FAQ 裡面發現更好的東西,直接讓終端機顏色支援 24 bit, 改好以後跑在終端機裡變這樣,跟 GUI 差不多:

Solarized Dark

是不是!根本是天與地的差別啊!有一點要注意的是,除了用 TERM=xterm-direct 來執行 emacs 確保在 -nw 情況下有效,emacsclient -t 時也要加上。

ADB_VENDOR_KEYS 檔案必須以 .adb_key 結尾

話說很久沒弄 Android 開發了,這兩天為了想把 Go 寫的東西包起來放上去跑,大費周章、不情不願的把 Android Studio 裝起來,結果要連上裝置的時候一直沒辦法認證成功。古董 Android 7 還沒有 WiFi pairing 的功能,所以本機產生的 adbkey 一直沒辦法被允許。想先用 USB 連,但目標是類似 Android TV 的東西,上面根本沒有 USB device 的接口。問廠商,還跟我說用對話框認證,就根本不會跳出來啊。後來他們把自己電腦裡的 adbkey 寄來,果然就可以了。但不想用他們的蓋掉我本機的,就照著 adb 說明,把寄來的 adbkey, adbkey.pub 另存一個目錄,然後設定環境變數 $ADB_VENDOR_KEYS 指到那目錄,卻怎麼也測不成功。

後來先把本機端 adb server 跑成 foreground, 命令用 env ADB_TRACE=all ./adb nodaemon server. 結果看到這神秘的幾行:

1
2
3
4
adb I 01-27 19:46:16 264189 264189 auth.cpp:408] watch descriptor 1 registered for '/home/john/.android/xxx
adb I 01-27 19:46:16 264189 264189 auth.cpp:158] load_keys '/home/john/.android/xxx'...
adb I 01-27 19:46:16 264189 264189 auth.cpp:194] skipped non-adb_key '/home/john/.android/xxx/adbkey.pub'
adb I 01-27 19:46:16 264189 264189 auth.cpp:194] skipped non-adb_key '/home/john/.android/xxx/adbkey'

non-adb_key 是什麼鬼?接下來去 github 找 adb 原始碼看到:

1
2
3
4
if (!android::base::EndsWith(name, ".adb_key")) {
LOG(INFO) << "skipped non-adb_key '" << path << "/" << name << "'";
continue;
}

好啊,一定要這樣就對了,我還以為它會每個檔案都讀讀看,結果是只讀 .adb_key 結尾的啊(參考)。把 xxx/adbkey 改成 xxx/xxx.adb_key 之後就可以了。