i3 WM (4) Run or raise で素早いアプリ切り替えと起動

i3 wm で Run or raise と 2ストロークショートカットキー

ウィンドウの切り替えを Alt+Tab のルーレットでやるのもいいけれど、よく使うアプリについては専用のキーで切り替えられると便利。さらに、もしそのアプリが起動していなければ起動してくれるとすごくありがたいでしょう。こういうのを Run or raise (Run or focus) などと呼ぶみたいだけれど、この機能を i3 上で実現したい。

それともうひとつ。キーボード・ショートカットは便利なのだけれども、キーの枯渇が問題。そこで Emacs のプレフィックスキーみたいに2ストロークでショートカットキーを設ける工夫もやってみる。

Run or raise (Run or focus)

i3 で Run or raise する工夫

i3 と同じタイル型ウィンドウマネージャの StumpWM には run-or-raise というコマンドがあって、アプリの起動あるいはフォーカスをこの一つがやってくれるものだから便利なのだ。

だけれども i3 にはこういう専用のファンクションはないようだ。しかしそこでググッと調べると、 python や javascript で書かれたありがたいスクリプト達がみつかる。さらにグググっといってみたら、とてもシンプルで素敵なワンライナーがあった。

… how about we just make the focus command report success properly? Currently it always reports “success”:true, but imagine it reported “success”:false, then the following could be used:

[ "$(i3-msg '[class=\"firefox\"] focus')" = "[{\"success\":true\}]" ] || i3-msg exec firefox`

(https://github.com/i3/i3/issues/1809#issuecomment-135656078)

これは、ある特定のウィンドウにフォーカスをあてる機能 (jumping_to_specific_windows) を使って、コマンドが返してくるステータスでそのアプリが起動済みかどうかを判断するということみたい。シンプルだが的を射ている。エクセレント。

簡単なスクリプトをかく

これをもとにrunorraiseコマンドをつくってみる。たいへん簡易なものだけれど、十分機能してくれると思う。

#!/bin/bash

if [ $# -eq 3 ]; then
    if [ "$(i3-msg "["$1"="$2"] focus")" != "[{\"success\":true}]" ]; then
        i3-msg exec "$3"
    fi
else
    i3-nagbar -t error -f "xft:DejaVu Sans Mono 11px" -m "${0##*/} : error (wrong number of arguments)"
    exit
fi

せっかくのワンライナー風味を台無しにしてしまったのだけれども、これを ~/bin/i3runorraise.sh に保存して実行属性をつけてあげる。

nano ~/bin/i3runorraise.sh
#----
chmod +x ~/bin/i3runorraise.sh

このスクリプトは引数3つを渡して実行させるようにしてあって、1つめは class / instance / title などのクリテリア、2つめにその値、3つめは起動コマンド。

ともかく Firefox でこれを試してみる。

i3runorraise.sh class Firefox firefox

このコマンドは、ウィンドウプロパティ ‘class’ の値が ‘Firefox’ となっているウィンドウがあればそれに切り替え、無ければ3つめの引数 ‘firefox’ を実行する、という意味。

つまりは Firefox が起動済みであったならばそれをアクティブにして、そうでなかったなら起動してきてくれるということだ。

ウィンドウのプロパティはxpropで調べる

これらの引数1つめと2つめの値は xprop を使って調べられる。

pacman -S xorg-xprop

試しにやってみる。

xprop を端末上で起動すると、マウスカーソルが十字に変わって xprop の待機状態となる。試しにカーソルを Firefox のウィンドウに重ねてクリックしてみると。

xprop
## Firefoxのウィンドウをクリックする
...
WM_CLASS(STRING) = "Navigator", "Firefox"
...
WM_NAME(STRING) = "Web Site hogehoge - Mozilla Firefox"
...

プロパティがドドッと表示されるけれど、その中の WM_CLASS(STRING) の左の値が instance 、右の値が class のよう。title は WM_NAME あたりだろう。ウィンドウの識別には、だいたいこの3つで足りるかと思う。

class と instance の組み合わせでウィンドウを識別することは、特定のウィンドウを特定のワークスペースに持って行ったり特定のウィンドウが開いたときに自動的にフローティングウィンドウにする、などといったカスタマイズで重要になる。

xterm にタイトルをつけて区別する

xtermなどの端末エミュレータは起動時に名前を決められるので、それを利用して複数端末の区別をつけられるようにできる。

xterm の起動オプション -T に続けて文字列を与えるとそれが title になる。それを織り込んで i3runorraise に渡してみる。

i3runorraise.sh title ^MyTerminal$ "xterm -T MyTerminal"

こうすると、title が MyTerminal となっているウィンドウを探し、なければ MyTerminal という名前で xterm が起動する。なお、単語 MyTerminal がブラウザのタイトルなどに入っているとそれと取り違えてしまうので、正規表現でマッチさせるようにした。

tmuxとかranger使うよっていうとき

私はurxvtにtmuxを組み合わせて使っているけれども(GnuScreen使っている人も大勢いるだろう)、xterm も urxvt も -e オプションで起動プログラムを指定できる。

# xtermの場合
i3runorraise.sh title ^MyTerminal$ "xterm -T MyTerminal -e tmux"; mode "default"
# urxvtの場合
i3runorraise.sh title ^MyTerminal$ "urxvt -title MyTerminal -e tmux"; mode "default"

あと、ファイラーは ranger という vim ライクなキーバインドの TUI ツール を使っているのだけれどとても便利だ。

pacman -S ranger
# xtermの場合
i3runorraise.sh title ^MyRanger$ "xterm -T MyRanger -e ranger"; mode "default"
# urxvtの場合
i3runorraise.sh title ^MyRanger$ "urxvt -title MyRanger -e ranger"; mode "default"

名前の付け方が My〜 になっててワンパターンだけどそこはよしなに。

mode を使って2ストロークショートカット

割り当てキーの枯渇への対処

あとは ここ でやったように bindsym でショートカットキーに設定すればできあがりだけれども、そのままでは当てるキーが空いてないんじゃないだろうか。$mod キーとの組み合わせなんか、使いやすいキーを中心に i3の機能で埋められているし。

選べるキーが少なければ、直感的に使えるキーの選定なんてほとんどできないんじゃない?

だから、ちょっとでもそれを解消するために 2ストロークショートカットキーを設定して、使えるキーを増やす工夫をしてみる。

モード機能

i3 の mode という仕組みでモードを切り替えると、キーバインドのセットも切り替わる。

言ってしまえば、普段の i3 が待機している状態は デフォルトモードであって、設定されているキーバインドもデフォルトモードのキーバインドセットと言える。そして、このデフォルトモード以外にも任意にモードをこしらえることができ、キーバインドはそのモード毎の設定となる。

前のところ でも書いたけれども、i3のウィンドウのリサイズはキーボードからでもできる。 $mod+r を押したあと j k l ; の各キーで伸縮させてから ENTERESC でリサイズ終了というやつだ。

実はこれもモードを使って実現されている。設定ファイルに mode “resize” { … } という設定がかかれてあって、このモードを $mod+r にバインドして実現しているのだ。

モードを設けてみる

あるモード内においてキーにバインドする命令内容を、命令実行後にモード終了させる、という風にしておくと、2ストロークのキーバインドになる。

例えば、 C-; に Commando という mode への切り替えをバインドして、Commando の中では C-b にブラウザの起動とモード終了(デフォルトモードに切り替え)を仕掛けてやる。すると C-; C-b という2ストロークのキーボード・ショートカットでブラウザの起動ができるようになる。そして、ブラウザの起動には先程の i3runorraise.sh をつかってあげるといいのだ。

bindsym Control+semicolon mode "Commando"
mode "Commando"{
    bindsym Control+b exec ~/bin/i3runorraise.sh class Firefox firefox; mode "default"
}

これで、 C-; C-b を押せばFirefoxに切り替わってくれるし、起動して無ければ起動もしてくれるということになる。

i3/config に設定する

ウィンドウスイッチャー Rofi の導入

Rofiでクールな軽量ミニマルランチャー」 のところで Rofi というウィンドウスイッチャーをやった。

このときの rofi_launch.sh を使って、ランチャー&スイッチャーをショートカットキーに割り当てると便利だ。 C-; C-; にあててみよう。

なお、透過表示を指定してあるが、Arch Linux だとそのままでは透過できないので、comptonを実行しておくようにする。

最初の回 ではインストールしてあるが、一応かくと、

pacman -S rofi compton

ワークスペース切り替えも

あと、ワークスペースの切り替えはデフォルトで $mod+<数字キー> だが、このキーバインドだとちょっと押しにくいと感じはじめると思うので、ここにもあてておくといいと思う。つまり Control+; <数字> でワークスペースを切り替えられるようにしておく。

以上をまとめて設定

以上のところまでを設定ファイル ~/.config/i3/config に書き加える。

# Left Alt
set $altl Mod1

# run compton
exec_always --no-startup-id compton

# Screen Lock
bindsym Control+$altl+l exec dm-tool lock

# Start MyCommand mode
set $MyCommand MyCommand mode  cancel: ESC
bindsym Control+semicolon mode "$MyCommand"

mode "$MyCommand"{
    bindsym Control+t exec ~/bin/i3runorraise.sh title ^MyTerminal$ "xterm -T MyTerminal"; mode "default"
    bindsym Control+e exec ~/bin/i3runorraise.sh class Emacs emacs; mode "default"
    bindsym Control+b exec ~/bin/i3runorraise.sh class Firefox firefox; mode "default"
    bindsym Control+f exec ~/bin/i3runorraise.sh title ^MyRanger$ "xterm -T MyRanger -e ranger"; mode "default"
    bindsym Control+semicolon exec ~/bin/rofi_launch.sh; mode "default"
    bindsym 1 workspace 1; mode "default"
    bindsym 2 workspace 2; mode "default"
    bindsym 3 workspace 3; mode "default"
    bindsym 4 workspace 4; mode "default"
    bindsym 5 workspace 5; mode "default"
    bindsym 6 workspace 6; mode "default"
    bindsym 7 workspace 7; mode "default"
    bindsym 8 workspace 8; mode "default"
    bindsym 9 workspace 9; mode "default"
    bindsym 0 workspace 10; mode "default"
    bindsym Escape mode "default"
    bindsym Control+g mode "default"
    bindsym Control+c mode "default"
    bindsym Control+bracketleft mode "default"
}

前の「i3 wm (3) カスタマイズ : ショートカットキーの設定」でやったキーボードロック ( Control+Alt+l ) を加えておいた。

エディタはワンパターンで Emacs にしてあるけれども、そこもよしなに(ちょっとみたら Gvim の class は Gvim だった)。

また、 C-; 押してからのキャンセルは ESC のほか C-gC-c それに C-[ にもあてておいた。

設定したキーバインドをまとめておくと以下のように。

キー 機能
C-; C-; タスクスイッチャ&ランチャ
C-; C-t 端末エミュレータに切り替え
C-; C-e エディタに切り替え
C-; C-b ブラウザに切り替え
C-; C-f ファイラーに切り替え
C-; <数字> ワークスペース #数字 に切り替え
Control+Alt+l キーボードロック

※キャンセルは ESC , C-g , C-c , C-[

プレフィクスの候補

モード発動に C-; ( Control+semicolon ) を選んだのは、私がショートカットキーとして使っていないキーの中で一番押しやすそうだったから。以前 emacs-helm の駆動に C-; を使っていたのだけれど、思うところあって丁度変更したあとだったのです。

プレフィクスを何にするかの候補としてよく言われるのは C-oC-tC-lC-zC-q それに、 C-,C-. あたりもそうだろうか。

あと、フルキーから離れてしまうので面白くないが、 F9 は空いていることが多いと思う。

おわりに

ここまであえて触れていないのだけれど、i3デフォルトの方向キーは「 j k l ; 」が「左下上右」で、 vi の 「 h j k l 」 とちょとちがう。私は vi ライクに変更してみているけれども、 vi キーバインドに浸かっていないのならばデフォルトの定義は直感的で、大いにアリかと思う。

なお vi ライクな h j k l にするなら splith のあてなおしが必要( $mod+V あるいは $mod+c にあてなおすといいかも)。また、デフォルトの設定ファイル内で変更すべき箇所は3箇所、すなわち ‘change focus’ ‘move focused window’ ‘mode resize’ 。

あと、参考URLの中に終了メニューをi3-nagbarと組み合わせて設ける例がある。これをrofiとの組み合わせでやると良さそうに思うのでそのうちに。

Revisions

  • [2016-07-20 Wed] compton を加えた
  • [2017-01-26 Thu] スクリプト訂正、コマンド引数訂正、語彙や言い回し調整