systemd上でxrandrが動かないのを解決した回

目的

systemd上でXアプリケーションを動作させる。

理由

Manjaro Linuxではモニターを繋いだだけでは自動でマルチモニタにならないため。マルチモニタを使う場合は xrandr を用いて

xrandr --output LVDS1 --auto --primary --output VGA1 --auto --above LVDS1

といったコマンドをいちいち打つ必要がある。面倒なのでコマンドをシェルスクリプトにしてモニターを繋ぐたびに手動でシェルスクリプトを実行していたが、ぶっちゃけめんどくさくなってきたので自動化しようと考えた。

提案手法

同じようなことを考えることはいるもので、下記のようにタイマー機能で1秒ごとにスクリプトを実行して切り替えを自動化している人がいることがわかった。

ArchLinuxで自動でdisplayの接続を認識し調整する

上記のページはウィンドウマネージャーとして awesome 環境を用いているが、自分は Xfce なので cron か systemd を検討した。自分の環境に cron はインストールしていないので環境を汚したくないなと思って systemd で自動化することに決めた。
systemd での自動実行には systemd.timer を用いればできることがわかったので、これについて調べた。

systemd.timer について

参考 systemd/タイマー - ArchWiki

ざっくりと言えば、<サービス名>.service と同じディレクトリに <サービス名>.timer を置いてやればいい。
systemctl daemon-reload も念の為忘れずに。

.service と .timer を書いた

autodualmonitor.service

[Unit]
Description = Automatic dual-single monitor switcher

[Service]
Type = oneshot
ExecStart = /home/<ユーザー名>/MyShellScript/auto_dualmonitor.sh
Requires=lightdm.service

autodualmonitor.timer

[Unit]
Description=Automatic dual-single monitor switcher : timer

[Timer]
OnBootSec=1min
OnUnitActiveSec=1sec

[Install]
WantedBy=timers.target

両方とも /etc/systemd/system ディレクトリに配置した。スクリプトは上記の ExecStart に書かれている通りの場所に配置した。

トラブルシューティング

systemctl daemon-reload

してから動くか試すために

sudo systemctl start autodualmonitor.service

を実行したがエラーが出て動作しなかった。 .timer もenableにしてみたが Active: failed (Result: exit-code) になっており、正常に動作していないことがわかった。

systemctl status autodualmonitor.service

でログを見てみたところ、 Can't open screen というログが見られた。どうやら xrandr が動いていないようだ。
調べてみたところ、Archlinux wiki の次のページに答えがあった。

systemd/ユーザー - ArchWiki

DISPLAY は X アプリケーションがどのディスプレイを使えばいいのか知るために使用されます。XAUTHORITY はユーザーの .Xauthority ファイルのパスと、X サーバーにアクセスするのに必要な cookie を指定します。systemd ユニットから X アプリケーションを起動する場合、これらの変数を設定する必要があります。バージョン 219 から、セッションが開始したときに DISPLAY と XAUTHORITY を systemd --user デーモンの環境にアップロードする X11 セッションアプレット /etc/X11/xinit/xinitrc.d/50-systemd-user.sh が systemd に付属するようになりました。これによって、X を標準の方法で起動しているかぎり、ユーザーサービスは DISPLAY や XAUTHORITY を使用することができるようになっています。<<

要約するに、DISPLAYとXAUTHORITYの環境変数が設定されていないとxrandrのようなXアプリケーションは動作しないということらしい。で、ターミナルから実行して動作するのに systemd では何故動作しないのかといえば、

systemd のユーザーインスタンスは .bashrc などに設定された環境変数を全く継承しません。<<

ということだった。つまりはシェルスクリプト内でDISPLAYとXAUTHORITYを設定してやれば動作すると見ていいだろう。そこで autodualmonitor.sh(モニター切り替え用のスクリプト) を次のように書き換えた。

autodualmonitor.sh

#!/bin/bash

IN="LVDS1"
EXT="VGA1"

export DISPLAY=:0.0
export XAUTHORITY="/home/<ユーザー名>/.Xauthority"

if (xrandr | grep "$EXT disconnected"); then
    xrandr --output $IN --auto $EXT --off
else
    xrandr --output $IN --auto --primary --output $EXT --auto --above $IN
fi

大抵の場合、DISPLAYは:0.0なので特に考えずにそのまま書いた。XAUTHORITYは echo $XAUTHORITY を実行して調べてきたものをそのまま書いた。

書き換えてから念の為 sudo systemctl daemon-reload し、systemctl start autodualmonitor.service がエラーを出さないことを確認してからPCを再起動した。

再起動後に systemctl status autodualmonitor.timer を実行して見てみると Active: active (waiting) となっており、モニターを抜き差ししてみると、少し時間を置いてモニターが接続されることが確認できた。

まとめ

  • Xアプリケーションを用いる際には DISPLAY と XAUTHORITY の環境変数が使われているので留意する。
  • systemd には .bashrc などで設定した環境変数を一切継承しないので留意する。
  • systemd 関係で詰まったときは systemctl status とシェルスクリプト上で echo を組み合わせてデバッグする。
  • やっぱりLinuxって面倒くさい