Ubuntu16.04LTSの画面を自動回転させる

# tl;dr
しばらく前に Dell inspiron 11 3147 を買ったのですが、Windows 10 がちょっと嫌になり、 Ubuntu 16.04 LTS を入れました。
各種ドライバーも使え、タッチパネルも使え、スクリーンキーボードも普通に使えています。
これだけでも充分満足なのですが、Windowsの時に使えていた画面の自動回転はUbuntuの設定にはないようで、どこの設定画面にもそれらしきものがありません。
とは言っても回転しなくてもどうということはないのです。
2-in-1のPCですがそもそもタブレットとしても使わないですし、タッチパネルもスクリーンキーボードも使えなくても全く問題ないのです。
最悪タッチパッドさえ使えれば別に良いってかんじです。
でもどうせなら自動で回転させたいですよね。
WindowsでできていたことをUbuntuでも同じようにしたいというのは至極当然の流れだと思います。

そこで画面の自動回転を検索したところ[江添さんのブログ](https://cpplover.blogspot.jp/2016/07/blog-post.html)がヒットしたので参考にさせてもらうことに。
で、色々ごちゃごちゃやってみてなんとか画面の自動回転が成功したので、記載します。


# 大まかな流れ
1. 必要なパッケージをインストール
2. 1でインストールしたツールをサービス化
3. rsyslogでログを取得する
4. ログを監視し、メッセージが出たら画面を回転させる簡単なスクリプトを書く
5. 4で書いたスクリプトをサービス化
6. 完了

こんな感じです。


# マシンの情報とか

|メーカ|シリーズ|OS|バージョン|GUI|
|---|---|---|---|---|
|Dell|Inspiron 11 3147|Ubuntu|16.04 LTS|Unity|

## カーネルバージョン

```shell-session
ine@daisy:~$ uname -a
Linux daisy 4.4.0-59-generic #80-Ubuntu SMP Fri Jan 6 17:47:47 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux
```

# 1. パッケージインストール

画面を自動で回転させるには、デフォルトでインストールされるであろう各種パッケージやコマンド群だけでは実現できません。
そのために必要なパッケージをインストールします。

また、画面の自動回転を実現するには、画面の傾きを検出する必要があります。
それを検出するためには加速度センサが内蔵されている必要があるみたいです。
今回のマシンはWindowsの時点で画面の自動回転が実現できていたので、センサがあるであろうと考えました。

## 画面を回転させるコマンド(xrandr)

画面の回転には `xrandr` というコマンドを使います。
UbuntuをGUIで使う場合は、デフォルトでインストールされているかも知れません、
入っていなければ `apt-get` で `xrandr` の入っているパッケージをインストールしてください。

```shell-session
ine@daisy:~$ sudo apt-get install x11-xserver-utils
```

これで `xrandr` コマンドが使えるようになりました。
試しに画面を回転させてみることにしましょう。

```bash
xrandr -o [normal|inverted|right|left]
```

`-o` の後に付けるオプションで回転が選択できます。

|オプション|回転|
|:-:|:--|
|normal|デフォルト(0度)に戻す|
|inverted|画面を180度回転させる|
|right|画面を右に90度回転させる|
|left|画面を左に90度回転させる|

以下のコマンドを打つと画面が右90度回転し、5秒後元に戻ります。

```shell-session
ine@daisy:~$ xrandr -o right && sleep 5 && xrandr -o normal
```

`xrandr` コマンドを使うとこんな感じで回転します。
このコマンドを使ってスクリプトを書き、自動化させてしまおうというわけです。

## 傾きを検出する(iio-sensor-proxy, monitor-sensor)

次に傾きを検出するツールをインストールします。
`apt-get` で `iio-sensor-proxy` というパッケージをインストールします。

```shell-session
ine@daisy:~$ sudo apt-get install iio-sensor-proxy
```

このパッケージにある `iio-sensor-proxy` と `monitor-sensor` というツールを使います。

インストールするとsystemdのユニットに `iio-sensor-proxy` というserviceファイルが追加されます。

```shell-session
ine@daisy:~$ systemctl list-unit-files iio-sensor-proxy.service
UNIT FILE                STATE 
iio-sensor-proxy.service static

1 unit files listed.
```

このserviceを起動した後、 `monitor-sensor` コマンドを実行すると、センサから読み取った情報が表示されます。


```shell-session
ine@daisy:~$ systemctl start iio-sensor-proxy
ine@daisy:~$ monitor-sensor
** Message: Light changed: 16.000000 (lux)
** Message: Light changed: 18.000000 (lux)
** Message: Light changed: 16.000000 (lux)
** Message: Light changed: 13.000000 (lux)
```

出力される数字がセンサから読み取っている情報のようで、少しでも角度が変わると数字が変わります。
出力されるのは数字だけではありません。
画面を回転させると、以下のようなメッセージが出力されます。

```shell-session
** Message: Light changed: 15.000000 (lux)
** Message: Light changed: 17.000000 (lux)
** Message: Accelerometer orientation changed: left-up
```

この `Accelerometer orientation changed: ` メッセージは、傾きがある値を超えると出力されるようです。
これにより画面がどの程度回転しているのかがわかります。

|出力|回転|
|:-:|---|
|normal|デフォルト(0度)|
|bottom-up|画面が180度回転した|
|right-up|画面が右に90度回転した|
|left-up|画面が左に90度回転した|

これで画面の傾きが検出できるようになりました。

# 2. `monitor-sensor` をサービス化

`iio-sensor-proxy` はserviceファイルもインストールされますが、 `monitor-sensor` はコマンドなので、適当にserviceファイルを作ります。
私は `/lib/systemd/system/` に `monitor-sensor.service` といったファイルで以下のような感じで書きました。

```bash:/lib/systemd/system/monitor-sensor.service
[Unit]
Description=Monitor Sensor service
Requires=iio-sensor-proxy.service
After=iio-sensor-proxy.service

[Service]
Type=simple
Restart=always
ExecStart=/usr/bin/monitor-sensor

[Install]
WantedBy=multi-user.target
```

ファイルを作ったら `systemctl` を再読み込みして、サービスの開始、自動起動を有効化しましょう

```shell-session
ine@daisy:~$ sudo systemctl daemon-reload
ine@daisy:~$ sudo systemctl start monitor-sensor.service
ine@daisy:~$ sudo systemctl enable monitor-sensor.service
Created symlink from /etc/systemd/system/multi-user.target.wants/monitor-sensor.service to /lib/systemd/system/monitor-sensor.service.
```

ステータスを確認すると起動していることが確認できると思います。

```shell-session
ine@daisy:~$ systemctl status monitor-sensor.service 
● monitor-sensor.service - Monitor Sensor service
   Loaded: loaded (/lib/systemd/system/monitor-sensor.service; enabled; vendor p
   Active: active (running) since 日 2017-01-29 22:11:40 JST; 1min 18s ago
 Main PID: 8276 (monitor-sensor)
    Tasks: 3
   Memory: 520.0K
      CPU: 35ms
   CGroup: /system.slice/monitor-sensor.service
           └─8276 /usr/bin/monitor-sensor

 1月 29 22:12:48 daisy monitor-sensor[8276]: ** Message: Light changed: 21.000000
 1月 29 22:12:49 daisy monitor-sensor[8276]: ** Message: Light changed: 20.000000
 1月 29 22:12:49 daisy monitor-sensor[8276]: ** Message: Light changed: 18.000000
```

これで `monitor-sensor` がOS起動時に自動で起動するようになりました。

# 3. rsyslogでログを取得する

Ubuntuではrsyslogでimuxsockモジュールをロードしているためか、systemd-journaldのログをrsyslogで取得できます。
これを使って `/etc/rsyslog.d` の下に以下のような設定ファイルを作ります。

```bash:/etc/rsyslog.d/10-monitor-sensor.conf
:msg,contains,"** Message: Accelerometer orientation changed:" -/var/log/monitor-sensor.log
& ~

:syslogtag,startswith,"monitor-sensor[" ~
```

これでOKです。
内容的には `"** Message: Accelerometer orientation changed:"` という文字列を含んだメッセージを `/var/log/monitor-sensor.log` に出力し、それ以外に `syslogtag` 部分に `monitor-sensor[` を含むメッセージがあれば全て除外する、といったものになります。

`"** Message: Accelerometer orientation changed:"` は `monitor-sensor` を動かした時、画面の傾きがある一定の値を超えると出力されるメッセージですね。
これだけをログに取るようにしました。
というのも、角度が変わるだけで出力されるメッセージも一緒のログに出力しているとログが肥大化してしまいそうだったためです。
結構な頻度で出てきますからね。
また、ファイル名を指定している直前にハイフン(`-`)が入っていますが、これがあるとメッセージ出力時に該当するファイルがない場合自動で作成してくれます。
とても便利。
最後の行はそれ以外のメッセージは除外するという意味のものです。

設定ファイルを作成したら `rsyslog` を再起動しましょう。

```bash
ine@daisy:~$ sudo systemctl restart rsyslog.service
```

一度 `/var/log` を見てみます。

```shell-session
ine@daisy:~$ ls -la /var/log/
drwxrwxr-x 12 root              syslog    4096  1月 30 23:19 .
drwxr-xr-x 14 root              root      4096  4月 26  2016 ..
-rw-r--r--  1 root              root     27811  1月 30 22:58 Xorg.0.log
.
.
.
drwxr-x---  2 root              adm       4096  1月 21 00:31 unattended-upgrades
drwxr-xr-x  2 root              root      4096  3月  1  2016 upstart
-rw-rw-r--  1 root              utmp    154752  1月 30 22:55 wtmp
```

`monitor-sensor.log` はまだないみたいです。
では、ログが出力されるようアクションをしてみましょう。
画面を物理的に90度回転させた後元に戻し、もう一度確認してみます。

```shell-session
ine@daisy:~$ ls -la /var/log/
drwxrwxr-x 12 root              syslog    4096  1月 30 23:19 .
drwxr-xr-x 14 root              root      4096  4月 26  2016 ..
-rw-r--r--  1 root              root     27811  1月 30 22:58 Xorg.0.log
.
.
-rw-r-----  1 syslog            adm        392  1月 30 23:19 monitor-sensor.log
.
.
drwxr-x---  2 root              adm       4096  1月 21 00:31 unattended-upgrades
drwxr-xr-x  2 root              root      4096  3月  1  2016 upstart
-rw-rw-r--  1 root              utmp    154752  1月 30 22:55 wtmp
```

作成されていることが確認できます。
umaskやowner、groupもrsyslogで設定されているものです。

内容も以下の通り

```shell-session
ine@daisy:~$ sudo cat /var/log/monitor-sensor.log
** Message: Accelerometer orientation changed: left-up
** Message: Accelerometer orientation changed: normal
```

これでログ設定はOKです。

# 4. ログを監視し、メッセージが出たら画面を回転させるスクリプト

最初は自分で一から作ろうと思ったのですが、探したら[それっぽいのがあった](https://linuxappfinder.com/blog/auto_screen_rotation_in_ubuntu)ので、使わせていただきました。
しかし元のままでは使えなかったため、多少改造したものが以下になります。

```bash:monitor-rotate.sh
#!/bin/bash
# Auto rotate screen based on device orientation
# Receives input from monitor-sensor (part of iio-sensor-proxy package)
# Screen orientation and launcher location is set based upon accelerometer position
# Launcher will be on the left in a landscape orientation and on the bottom in a portrait orientation
# This script should be added to startup applications for the user
# Parse output or monitor sensor to get the new orientation whenever the log file is updated
# Possibles are: normal, bottom-up, right-up, left-up
# Light data will be ignored

WATCH_LOG="/var/log/monitor-sensor.log"
LAUNCHER_POS="set com.canonical.Unity.Launcher launcher-position"

export XAUTHORITY=${HOME}/.Xauthority
export DISPLAY=$(w -hs | grep ${LOGNAME} | awk '{print $3}')
export DISPLAY=${DISPLAY:-\:0}

while inotifywait -e modify ${WATCH_LOG} > /dev/null 2>&1 ; do
  # Read the last line that was added to the file and get the orientation
  ORIENTATION=$(tail -n 1 ${WATCH_LOG} | grep -oE '[^ ]+$')

  # Set the actions to be taken for each possible orientation
  case "${ORIENTATION}" in
    normal)
      xrandr -o normal && \
      gsettings ${LAUNCHER_POS} Left
      echo "Orientation Changed normal"
    ;;

    bottom-up)
      xrandr -o inverted && \
      gsettings ${LAUNCHER_POS} Left
      echo "Orientation Changed inverted"
    ;;

    right-up)
      xrandr -o right && \
      gsettings ${LAUNCHER_POS} Left
      echo "Orientation Changed right"
    ;;

    left-up)
      xrandr -o left && \
      gsettings ${LAUNCHER_POS} Left
      echo "Orientation Changed left"
    ;;

  esac

done
```

while文でログを確認してグルグル回すスクリプトです。
ログに特定の文字列(normal|bottom-up|right-up|left-up)が出た時に `xrandr` で画面を回転させます。
文字列の検知には `inotifywait` コマンドを使っています。
オプションには `-e modify` を使っていますが、これは**「指定したファイルの内容に変更があった場合に通知する」**という意味のものになります。
これのおかげで `/var/log/monitor-sensor.log` の内容に変更(書き込みや削除等)があったというイベントが走る度に `while` が回るようになっています。
更にそのメッセージから文字列を切り出して分岐させている・・・という具合です。

これをserviceファイルにして自動起動させるようにします。

## 設定されない環境変数

これは自分の環境にあわせた結果ですが、
スクリプトの初っぱなに環境変数を定義している部分があります。

```bash
export XAUTHORITY=${HOME}/.Xauthority
export DISPLAY=$(w -hs | grep ${LOGNAME} | awk '{print $3}')
```

不要っぽいですが、ないとちょっとまずいんです。
というのも、最初これを入れずにservice化させた時、以下のようなエラーが出て自動起動できなかったためです。

```shell-session
 1月 30 21:28:25 daisy monitor-rotate.sh[1446]: Can't open display
```

調べたところ、どうやら環境変数の `DISPLAY` と `XAUTHORITY` が設定されていないと上記のエラーが出てしまうようでした。
で、結構調べたんですが、 **systemd で各種サービスが読み込まれる時点では、上記の環境変数って未設定っぽい**のですね。
どうやらXセッションが開始すると設定される環境変数のようで、`/etc/X11/Xsession.d` 以下にあるファイルに変数が定義されていました。
自動起動をさせたいのですが、これだと困ります。
そのため、スクリプト内で環境変数を設定してしまおうと考えたわけです。

`XAUTHORITY` はホームディレクトリにコピーされる.Xauthorityを見ているらしいので、 `XAUTHORITY=${HOME}/.Xauthority` としました。
`DISPLAY` はどうすればいいのかわからなかったのですが、どうやら `w` コマンドで出力されるFROMに書かれているものと同一のようだったので、grepとawkを使って切り出し、 `DISPLAY=$(w -hs | grep ${LOGNAME} | awk '{print $3}')` といった感じになりました。
ちなみに `HOME` と `LOGNAME` は `monitor-rotate.sh` を起動する以前に設定されているようでした。

あとは `export` で環境変数とすれば完璧！
と思っていたのですが、この記事を書いた後、何度か起動していたら何故か動かなくなっていました。
原因を調べたところまたも `DISPLAY` が定義されていなかったので、以下を足しました。

```bash
export DISPLAY=${DISPLAY:-\:0}
```

多分これで上手く動くんじゃないですかね・・・(願望)。

これに結構時間食いました。
でも、何はともあれ完成です。

作ったスクリプトは恐らくパスが通っているであろう `/usr/local/bin/` ディレクトリにでもコピーしておきましょう。

```shell-session
ine@daisy:~$ sudo cp -ip monitor-rotate.sh /usr/local/bin/
```

# 5. `monitor-rotate.sh` をサービス化

最後に `monitor-rotate.sh` をserviceファイルにします。
普通にservice化させるのであれば、全ユーザに適用させたいので `monitor-sensor.service` を作成した時と同様に `/lib/systemd/system/` ディレクトリに作ってしまえば良いのですが、今回はユーザ固有の環境変数(`HOME`, `LOGNAME`)を使っていますし、スクリプト内で設定している `DISPLAY` も毎度 `:0` とは限りません。
ではどうするかですが、 systemd はユーザに systemd のインスタンスを実行させてサービスを管理できる機能を提供しています。
ココらへんは [Arch Linux の Wiki](https://wiki.archlinuxjp.org/index.php/Systemd/ユーザー) に詳しく書いてあります。
要は **ユーザ固有の権限で停止、起動等ができるserviceファイルを作れる** というお話です。

`/usr/lib/systemd/uesr/` ディレクトリの下に、以下のserviceファイルを作ります。

```bash:/usr/lib/systemd/uesr/monitor-rotate.service
[Unit]
Description=Monitor Rotation Service

[Service]
Type=simple
ExecStart=/usr/local/bin/monitor-rotate.sh

[Install]
WantedBy=default.target
```

作成したら、systemctl を再読み込みして、サービスの開始、自動起動を有効化しましょう。
気を付ける点としては、 **`systemctl` コマンドの直後に `--user` を付けなくてはいけない**ということです。

```shell-session
ine@daisy:~$ sudo systemctl --user daemon-reload
ine@daisy:~$ sudo systemctl --user start monitor-sensor.service
ine@daisy:~$ sudo systemctl --user enable monitor-sensor.service
Created symlink from /home/ine/.config/systemd/user/default.target.wants/monitor-rotate.service to /usr/lib/systemd/user/monitor-sensor.service.
```

自動起動を有効にするとホームディレクトリの下に `.config` ディレクトリが作成され、シンボリックリンクが貼られます。
これで自動起動設定まで完了です。
再起動してからPCを傾けてみてください。

# デモ的な

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">傾きで自動回転してくれるようになったぞい <a href="https://t.co/RJPwKkPZJd">pic.twitter.com/RJPwKkPZJd</a></p>&mdash; イネ (@i_luv_kneesox) <a href="https://twitter.com/i_luv_kneesox/status/826432990666334209">2017年1月31日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

こんな感じになるはずです。
描画が遅いのはUnityだからというのと、PC自体のスペックの問題かもしれません。
現状こんな感じです。
回転時にブラックアウトするのがちょっと気になりますが、まぁ及第点かな、と思います。

# 所感

まぁ及第点ではあるのですが、もうちょっとやりようがあったんじゃないかなと思ったりもします。
なんか色んな所にファイル作り過ぎなんですよね。
手順もしんどいし・・・なんなんですかね・・・。
とりあえずこれが私の限界です。
ちなみに、画面を手動で回転させるのであれば[GitHubにScreenRotator](https://github.com/frecel/ScreenRotator)というリポジトリがありました。
DLしてファイルを実行させるだけなので用途によってはこっちの方が良いかもしれません。
アイコンも表示されて、ちょっといい感じです。
できればこれを真似して自動回転させるスクリプトとか書ければよかったんですが、python・・・わっかんねえんだよなぁ・・・。
精進します。

以上。