tmux
tmuxのコピペは便利につかえるが、ひと工夫することでより便利に。
bind-key [ copy-mode bind-key C-[ copy-mode bind-key C-] choose-buffer "paste-buffer -p -b %%" bind-key ] choose-buffer
choose-bufferをつかうことでペーストしたいバッファをインタラクティブに選べて、不要になったバッファを消すこともできる。
paste-buffer -pオプションで(アプリがサポートしていれば)bracketed paste modeをつかって、エディタに直接ペーストできるので、たとえば普通にペーストするとインデントがずれてしまうところ、braceted paste modeをつかうとエディタが(キー入力じゃなくて)ペーストだと認識できるのでインデントがずれないし、ペーストをUNDOできる(キー入力扱いだと一発でUNDOできない)、巨大なテキストを貼り付けた場合には処理時間が短くて済む利点も。
tmux2.3でpane-border-status/pane-border-formatが追加されてpane_titleが表示できるようになった。便利かもしれない。うっとおしいだけかもしれな。
* New option 'pane-border-status' to add text in the pane borders.
設定はこんなかんじで。
tmux set -g pane-border-status bottom tmux set -g pane-border-format "#{?pane_active,#[reverse],}#{pane_index}:#{pane_title}"
タイトルを設定するエスケープシーケンスを出すと反映される。
printf "\033]2;`hostname`:`/bin/pwd`\033\\"
tmux list-windows ででてくるlayout情報は tmux select-layout で復元できるが、フォーマットがマニュアルには書いてないのでソースコードをみてみた。
% tmux list-windows 0: emacs (1 panes) [309x91] [layout bd46,309x91,0,0,9] @4 1: ssh (1 panes) [309x91] [layout bd45,309x91,0,0,8] @3 2: ssh (18 panes) [309x91] [layout bbb8,309x91,0,0[309x18,0,0{77x18,0,0,10,77x18,78,0,11,76x18,156,0,12,76x18,233,0,13},309x18,0,19{77x18,0,19,14,77x18,78,19,15,76x18,156,19,16,76x18,233,19,17},309x17,0,38{77x17,0,38,18,77x17,78,38,19,76x17,156,38,20,76x17,233,38,21},309x17,0,56{77x17,0,56,22,77x17,78,56,23,76x17,156,56,24,76x17,233,56,25},309x17,0,74{77x17,0,74,26,231x17,78,74,27}]] @5 3: tcsh- (1 panes) [309x91] [layout ded0,309x91,0,0,40] @12 4: tcsh* (1 panes) [309x91] [layout ded4,309x91,0,0,44] @13 (active) %
最初の4文字は16進数でカンマ以降の内容のチェックサムになっている。チェックサムルーチンは layout-custom.cのlayout_checksum()でローテートしながら文字を足し込んでいくだけの簡単なもの。ちょっと気になるのは、文字をcharで扱っているのでMSBが立っているとcharがsignedかunsignedかで符号拡張でチェックサムが違ってきてマシン間でポータブルじゃないが、そもそもペーンIDが埋め込まれているのでポータブルである必要がなかったし、そもそもasciiしかつかってないのでok。
レイアウト ::= チェックサム "," レイアウト情報 レイアウト情報 ::= コンテナ | 単体 コンテナ ::= 横幅 "x" 高さ "," 左上X座標 "," 左上Y座標 "," 内部 単体 ::= 横幅 "x" 高さ "," 左上X座標 "," 左上Y座標 "," PANE_ID 内部 ::= 上下分割 | 左右分割 上下分割 ::= "[" レイアウト情報 "]" 左右分割 ::= "{" レイアウト情報 "}"
ここでの上下分割はtmux split-window -v、左右分割はtmux split-window -h。これもどっちが縦でどっちが横なのかわかりにくいので、日本語なら上下分割・左右分割と呼ぶのがいいとおもうなぁ。
paneをzoomしているとそのpaneがウィンドウを専有するレイアウトが出力されるんだなぁ。でもこのズーム状態をselect-layoutで復元することはできないようだ。なんか謎。
tmuxでウィンドウの順番は swap-window をつかえばできるが、もっとvisuallyにやるスクリプト。$HOME/.tmux.confで適当なキーにわりあててつかっている。
bind-key W run "tmux-swap-windows"シェルスクリプトtmux-swap-windowsを実行すると編集用のウィンドウが1つできて、その中でviが起動してwindowのリストを編集してZZで終了すると、その順序でウィンドウを並べ替える。ウィンドウ名を書き換えると反映される小ネタもある。

grouped sessionをつかうとscreenでのattachとおなじように、カレント・ウィンドウを独立に切り替えらえるようになる。
それでもscreenとはちがって
- screenは端末ごとにウィンドウのレイアウトを変えられるが、tmuxはpaneの配置は共有される。
- tmuxのウィンドウサイズは一番小さな端末の画面サイズになる。screenは fit コマンドをつかえば任意の端末の画面サイズに合わせられる。
grouped sessionのつかいかた
まずは端末エミュレータを2枚ひらく。
1枚目でtmuxコマンドを実行してセッションをつくる。ついでに C-b c でウィンドウをもうひとつ作っておく。
tmux lsを実行するとセッションの一覧がでてくる。こんなかんじ。左端がセッション名になる。
% tmux ls 0: 2 windows (created Thu Dec 27 19:00:07 2012) [80x23] (attached) %
つづけて2枚目で tmux new-session -s hoge -t 0 を実行すると
一見 tmux attach した場合と同じようにみえる。
でも current window を独立に切り替えられるところが違う。
% tmux ls 0: 2 windows (created Thu Dec 27 19:00:07 2012) [80x23] (group 0) (attached) hoge: 2 windows (created Thu Dec 27 19:02:38 2012) [80x23] (group 0) (attached) %
2枚目で起動したtmuxセッションを終わるときは tmux kill-session を実行する。
detachしただけだとセッションが残ってしまうことに注意。
あと、tmux set -g aggressive-resize on を設定しておくのが吉。
mltermのなかでtmuxをうごかしていて、ログをがばっと出力するときによく暴走するので調べてみた。
- truss -pでみてもsyscallは発行していない。
- gdbでアタッチして関数をみると奥深いところでクルクルしてるっぽい。
- ソースコードからつくってgdbの中で実行→暴走させてC-cで止めてみると
- flush_scroll_cache()がml_term_set_modified_lines()を呼び出すときの第3引数がデカい。
- screen->scroll_cache_boundary_start = 27
- screen->scroll_cache_boundary_end = 44
- screen->scroll_cache_rows = 76
- となってたので 仮引数 end = 4294967264。
- flush_scroll_cache()がml_term_set_modified_lines()を呼び出すときの第3引数がデカい。
対症療法:
diff --git a/xwindow/x_screen.c b/xwindow/x_screen.c --- a/xwindow/x_screen.c +++ b/xwindow/x_screen.c @@ -755,6 +755,7 @@ /* * scrolling upward. */ + if (screen->scroll_cache_boundary_end >= screen->scroll_cache_rows) ml_term_set_modified_lines( screen->term , screen->scroll_cache_boundary_start , screen->scroll_cache_boundary_end - screen->scroll_cache_rows) ; } @@ -763,6 +764,7 @@ /* * scrolling downward. */ + if (screen->scroll_cache_boundary_start >= screen->scroll_cache_rows) ml_term_set_modified_lines( screen->term , screen->scroll_cache_boundary_start - screen->scroll_cache_rows , screen->scroll_cache_boundary_end) ;
mltermの新しいのにしたらこのへんのコードがかわってた。でも同じだった。
(gdb) p screen->scroll_cache_boundary_start $1 = 39 (gdb) p screen->scroll_cache_boundary_end $2 = 76 (gdb) p screen->scroll_cache_rows $3 = 0 (gdb) p scroll_cache_rows $6 = 87
暴走させるコツがわかってきた。
- こんなスクリプトを用意しておく(ファイル名は仮にhoge.shとして保存しとく)
#!/bin/sh I=100 while : ; do echo $I I=$((I+1)) done
- mltermをたちあげて広くする。全画面とか。手元の環境では 92 rows; 153 columns; になる (stty -a調べ)
- tmuxをたちあげる。
- tmux split
- tmux set sync
- sh ./hoge.sh
- 一丁あがり!
mltermの最新版をビルドしようとしたらlinkでエラーになった。FreeBSDの問題なのかmltermの問題なのかわからんがパッチ:
diff --git a/main/dexport.map b/main/dexport.map --- a/main/dexport.map +++ b/main/dexport.map @@ -2,6 +2,9 @@ global: x_imagelib_load_file ; x_get_opened_displays ; + # KOIEMEMO: /usr/bin/ld: mlterm: local symbol `__progname' in /usr/lib/crt1.o is referenced by DSO + __progname ; + environ ; local: * ; } ;
[2012-09-18 12:39]
arakiken@arakiken
@koie 御指摘ありがとうございます。この問題は、CSI rと大量のログ出力がうまく重なったときしか顕在化しないため、気付いていませんでした。http://t.co/EirtNPBe のとおり修正しました。
2012/09/15 21:08:34
作者さんから直ったとの連絡をいただきました!
http://mlterm.sourceforge.net/mlterm-3.1.3post-fixscroll.patch のパッチを /usr/ports/x11/mlterm/files/patch-mlterm-3.1.3post-fixscroll に置いて make clean && make && make deinstall && make reinstall で入れ替え。
automatic-rename on で使っているとステータスラインのwindow nameにそのとき実行しているプロセス名が表示されるが、makeなんかしているときは表示が頻繁に変わって目障りでもある。
いったいどんなふうにプロセス名を決めているのかtmux/osdep-freebsd.c (r2849)をよんでみると、
- ウィンドウのアクティブ・ペーンの端末を対象に
- 端末のフォアグラウンド・プロセス・グループID(=グループ・リーダー)を調べて(tcgetpgrp)
- sysctlでプロセスグループに属するプロセスの一覧をとってきて
- グループ内プロセスの中からいちばんいいプロセスを選択
- ただしプロセスの制御端末がペーンの端末になっていること
tmux的にいちばんいいプロセスとは、BSD系だとこんなかんじ:
- SRUN(実行可能) or SIDL(プロセスができたばかり)なのがよい
- SSTOP(C-zとかデバッガで止められている) or SZOMB(死んでる)なのはだめ
- CPUを使っている方がよい(psコマンドのcpuで出てくるやつ) (dragonflyにはない)
- 寝てる時間が多いのはだめ (dragonflyにはない)
- P_SINTR(寝てるけど割り込み可)なのがよい (openbsdのみ)
- それでも差がつかなかったら、コマンド名が辞書順で先なのを選ぶ (netbsdにはない)
- それも同じだったら、pidが大きい方を選ぶ
この大袈裟なロジックのおかげでmakeを走らせていると頻繁にステータスラインが変化するんだな。
osdep-darwin.cだと端末のフォアグラウンド・プロセス・グループ・リーダー p_comm を表示、osdep-linux.cだと端末のフォアグラウンド・プロセス・グループ・リーダーの/proc/.../cmdlineからargv[0]を表示するようだ。これならmakeを実行中はずっと make と表示されるはずだ。BSD系もこのくらいでいいんじゃないか?
tmux-zoomをつかっていたがrotate-windowに耐性がないことに気づいてしまったのと、pane_indexではなくてpane_idをつかえばよさそうというアイデアがうかんだのでスクリプトを書いてみた。
pane_indexとpane_idの関係はこんなかんじ↓になっている。tmux rotate-windowを実行するとpane_indexは上から順番に0,1,2のままだがpane_idは上に1つずれる。
window_index=0 +---------------+ | pane_index=0 | | pane_id=%0 | | | | | +---------------+ | pane_index=1 | | pane_id=%1 | | (active) | | | +---------------+ | pane_index=2 | | pane_id=%2 | | | | | +---------------+ | | tmxu rotate_window V window_index=0 +---------------+ | pane_index=0 | | pane_id=%1 | | (active) | | | +---------------+ | pane_index=1 | | pane_id=%2 | | | | | +---------------+ | pane_index=2 | | pane_id=%0 | | | | | +---------------+
この様子は tmux list-panes -a をみてるとわかる。
スクリプトを書いてて気づいたのはpaneはtty(とプロセス)に紐付いているのであってwindowの矩形領域のことではない。swap-paneでいれかわるのは矩形領域とttyのつながりである。pane_indexが矩形領域の識別子で、pane_idがtty/processの識別子ということのようだ。
window_index=0 window_index=1 +---------------+ +---------------+ | pane_index=0 | | pane_index=0 | | pane_id=%0 | | pane_id=%3 | | | | | | | | | +---------------+ | | | pane_index=1 | | | | pane_id=%1 | | | | | | | | | | | +---------------+ | | | pane_index=2 | | | | pane_id=%2 | | | | | | | | | | | +---------------+ +---------------+ | | swap-pane -s %0 -t %3 | V window_index=0 window_index=1 +---------------+ +---------------+ | pane_index=0 | | pane_index=0 | | pane_id=%3 | | pane_id=%0 | | | | | | | | | +---------------+ | | | pane_index=1 | | | | pane_id=%1 | | | | | | | | | | | +---------------+ | | | pane_index=2 | | | | pane_id=%2 | | | | | | | | | | | +---------------+ +---------------+