sshの接続先ごとにターミナルの背景色を変更する (mosh+tmux+ダークモード版)

# 皆サーン、間違ってssh先の本番マシンを落としたことはアリマスカー？ 私はアリマース！

ssh先に応じてイロイロ色を変更する、という安全策は古今東西たくさんありますね。
プロンプト、iterm2のprofileなど...

[https://qiita.com/search?q=ssh+色](https://qiita.com/search?q=ssh+%E8%89%B2)

そんなn番煎じですが、今回はtmuxの機能を用いて背景色を変更し、ついでに昼と夜で別の色にします（いわゆるダークモード対応）。

# 概要

- ノートパソコンを地下鉄や新幹線といった場所で使っていても、安定して遠隔マシンを操作
- 遠隔マシンから更に別のマシンにsshしたとき、背景色を変える
- 昼と夜では別の色

```mermaid
stateDiagram-v2
direction LR

    iterm2 --> mosh_client
    mosh_client --> mosh_server : mosh
    mosh_server --> tmux
    tmux --> hostC : ssh
    tmux --> hostD : ssh
    tmux --> hostE : ssh

    state hostA {
      iterm2
      mosh_client
    }
    state hostB {
      tmux
      mosh_server
      sunwait
   }

```

hostB, hostC, hostD, hostE... で背景色を変えます。

もちろんhostAの中でtmuxを使っている時も有効なお話です。

# レシピ

- フルカラー表示可能なターミナル
    - 今回はmacos iterm2としていますが、ghosttyやその他端末、OSでも適用可能です
- mosh
    - etやssh over udp類では新幹線車内など不安定な回線では使いにくいため、古式ゆかしくudp+仮想フレームバッファのmoshを使います
    - iterm2のprofile切り替えOSCはmoshを通りませんので、遠隔側で色を変更します
- tmux
    - gnu screenでもいいのですが、ここはモダンにtmuxを使います
- sunwait
    - iterm2のprofileを切り替える場合、昼夜のライトテーマ、ダークテーマの自動切り替えが可能となります
    - しかし(遠隔の)tmuxで背景色を設定する場合はその機能が使えません
    - そこで、tmuxが動いているホストの中で昼夜の判定をします
    - iterm2が動いているマシンと物理的に距離がある場合は時差が発生しますので海外出張の時は緯度経度を端末から渡す操作が必要となることでしょう
    - tmuxと同じマシンに入れる必要があります (hostAでtmuxなら、hostAに入れる）

では以下、個別に...

## mosh

「mosh捨ててetだ！」という声もありますが、列車内など安定しない回線での利便性ではmoshに一日の長があります。
etは復旧に10秒程かかりますが、moshは1秒も待たずに復旧してくれるので、vpnの復旧の時間と併せても数秒でプロンプトと戯れることができます。
さらに（擬似的）ローカルエコーバック機能があるため、RTTが大きな環境でのタイピングでも違和感は少なくなります（体感なので個人差もあります）。

そのmoshですが、iterm2のOSC 1337のprofile変更を通してくれません。

https://iterm2.com/documentation-escape-codes.html

etは通してくれます。
moshでも通すようにするパッチもありますが、メンテナンスされていないようですので今回はtmux側で色を変更することにします。

https://donnlee.com/2018/03/31/mosh-with-iterm2s-tmux-integration/

https://github.com/4ast/mosh

## sunwait

緯度・経度から日の出・日没時刻を計算してくれます。

https://github.com/risacher/sunwait

make一発。
macosの自動ダークモード・ライトモード切り替え時刻とは微妙に一致しない[^1]こともありますが、そこはご愛嬌。

[^1]: 緯度経度を高精度に指定すれば一致するのかもしれません。薄明の扱いかもしれません。

## tmux

### 24bitカラー対応

もう既に設定されている方が大半かとは存じますが、24bit色を表示可能にしておきます。

https://kakurasan.blogspot.com/2017/06/term-256-and-16million-colors.html

```conf:~/.tmux.conf
set -g default-terminal "xterm-256color"
set -ga terminal-overrides ",xterm-256color:Tc"
```

無事16M色になったかの確認はこちらで。

```sh
curl -s https://raw.githubusercontent.com/JohnMorales/dotfiles/master/colors/24-bit-color.sh | bash
```

### windowタイトル設定

こちらも多くの方はすでにプロンプトと併せて設定されているかとは思いますが、sshしたときにwindowタイトルをホスト名を含むものにします。
今回は背景色の記事ですが、背景色設定スクリプトではwindowタイトルから色を決定していますので、windowタイトルも変更します。

sshをラップするshell functionまたはスクリプト等を用意し、ssh前後で設定します。 tmuxのwindowタイトルの変更は、

```shell:
echo -n -e "\033kタイトル文字列\033\\"
```

となります。

https://github.com/tmux/tmux/issues/1322

# tmuxのwindowタイトルから、tmuxの背景色を設定するスクリプト

window単位で変えます。

https://qiita.com/mnishiguchi/items/210453c5778769df369a

```sh:change-tmux-color.sh
#!/bin/sh

# tmuxのwindow_nameに基づいて、背景色を変える

target="$1"
if [[ "$target" ]]; then
  targetarg="-t $target"
fi

windowname=$(tmux display $targetarg -p '#{window_name}')

light="default"
dark="default"

case "$windowname" in
  *${HOSTNAME}:*)
    # 同じならdefault
    light="default"
    dark="default"
    ;;
  *hoge:*)
    # solarized
    light="#fcf6e2"
    dark="#02232b"
    ;;
  *fuga:*)
    # solarizedを少し緑気味に
    light="#f6fcf6"
    dark="#022b23"
    ;;
  *piyo:*)
    # solarizedを少し赤気味に
    light="#fef6fe"
    dark="#2b0223"
    ;;
esac

# 東京の緯度・経度
if sunwait report 35.7N 139.7E | grep -q "It is: Day"; then
  tmux set $targetarg window-active-style "bg=$light"
else
  tmux set $targetarg window-active-style "bg=$dark"
fi

```

こんな感じのスクリプトを用意し、windowタイトル変更後に呼びます。

# 長時間ssh対応

このままですと、昼間にsshすると日没後もlightなままになります。
`bg=default`の場合はiterm2の設定で昼夜テーマが反映されますが、tmuxで明示的設定をしている場合はテーマを上書きしてしまいます。

1.  tmuxの適当なhookを使って変更
    - 例えばウィンドウを切り替える度に更新するとしたら、`set-hook -g session-window-changed 'run-shell "tmux-change-color.sh"'`
2.  手作業で変更する
    - tmuxの`bind-key`や`display-menu`の中で`run-shell`する
3.  sshラッパーの中で時限爆弾を設置

でもいいのですが、日の出・日没ごとに全windowを一括で更新するスクリプトを裏で動かしておくか、

```sh:update-windowbg.sh
#!/bin/sh

while sunwait wait 35.7N 139.7E; do
  tmux list-windows -F '#{window_id}' | while read -r windowid ; do
    change-tmux-color.sh $windowid
  done
done
```

cronで毎日12時と24時に `sunwait wait ; tmux list-windows ...` と実行します（白夜・極夜がある場合を除く）。
なお、sunwait waitした直後にsunwait reportをするとズレることがありますので注意。

ちなみに、遠隔のtmuxではなく手元(hostA)で動作するtmuxかつmacosであれば、こちらのpluginのような方法で変更できます。

https://github.com/erikw/tmux-dark-notify

# 免責事項😆

本記事の設定が正しく動作するか否かに関わらず、利用者が意図しない遠隔マシンを吹っ飛ばしたことによる損害に対して本記事の筆者は何ら責任を負いません。