xmonadのマルチディスプレイ環境で壁紙のローリングをするスクリプトを重複起動せずに自動で動作させる

# 概要

- 壁紙に色々と用意しているので，背景を自動でスライドショーさせたい
    - そこで，xmonadのstartupHookに所望のshell scriptを指定してループさせることにする
    - ただし，接続されたディスプレイ環境によりnitrogenでマルチディスプレイをぶち抜きにしたいマシンと，なんか分からんけどnitrogenがうまく設定できずfehを使う必要があるマシンがある
- 私の環境ではログアウト→ログインで多重起動してしまうので，重複起動チェックを入れた
- 私の環境ではtrapを仕掛けたところで，そもそもスクリプトがkillで停止してくれないので，crontabも併用する必要があった

# 参考文献

- 重複起動を防ごうとして参考にしたサイト
    - [シェルスクリプトの二重起動禁止 \- 祈れ、そして働け ～ Ora et labora](http://tetsuyai.hatenablog.com/entry/20110720/1311131803)
- （自分の記事）[nitrogenでデュアルディスプレイの壁紙をぶち抜きでランダム更新 \- Qiita](https://qiita.com/hadacchi/items/0257651944a474f62951)


# set background
## nitrogenとfehの振り分け

```sh
    if type nitrogen > /dev/null 2>&1; then
        # procedures for nitrogen
    else
        if type feh > /dev/null 2>&1; then
            # procedures for feh
        fi
    fi
```

nitrogenとfehそれぞれ一方しか使えない場合があるが，スクリプトを2種類用意するのが面倒なので，コマンドの存在で振り分けることとした．
インストールしたら使うというdebian文化を信じることにする．（ご都合主義）

## ランダム画像選択

```shell
random_pick() {
    JPGLIST=$(find ${PICDIR} -type f)
    NUM_JPG=$(echo "$JPGLIST" | wc -l)
    RAND=$(expr $(date +%s) % ${NUM_JPG} + 1)
    SRC=$(echo "$JPGLIST" | head -${RAND} | tail -1)

    cp -f ${SRC} $1
}

random_pick $TARGETFILE
```

`nitrogen`の背景ランダム化に`--restore`を用いているため，固定パスの画像をランダムに選択した画像で強制上書きすることにした．
それに合わせ`feh`も同じ方式に統一しランダマイズは一つの実装で行なうこととした．
固定パスを各々のソフト用のディレクトリ内としたため，`random_pick`関数の引数のパスにランダムに選定された画像を上書きコピーするように実装した．

## set-background.sh

```shell
#!/bin/sh

# if you wanna set a single background over multiple display, you should choose nitrogen.

PICDIR=~/Pictures/wallpapers

interval=900

_lockfile=/tmp/$(basename $0.lock)

random_pick() {
    JPGLIST=$(find ${PICDIR} -type f)
    NUM_JPG=$(echo "$JPGLIST" | wc -l)
    RAND=$(expr $(date +%s) % ${NUM_JPG} + 1)
    SRC=$(echo "$JPGLIST" | head -${RAND} | tail -1)

    cp -f ${SRC} $1
}

ln -s /dummy $_lockfile 2> /dev/null || { echo 'Cannot run multiple instance.' >&2; exit 9; }

trap "rm $_lockfile; exit" 1 2 3 15

# following sentences don't work if `sh -c $0' exists.
#if [ $$ = $(pgrep -fo $0) ]; then
    if type nitrogen > /dev/null 2>&1; then
        EXEC=nitrogen
        TARGETFILE=$HOME/.config/nitrogen/bg.jpg
        if [ ! -e ~/.config/nitrogen/bg-saved.cfg ]; then
            echo First of all you have to setup ~/.config/nitrogen/bg-saved.cfg
            echo Next, change 'file' value into \$TARGETFILE.
        else
            while true
            do
                random_pick $TARGETFILE
                $EXEC --restore
                sleep $interval
            done
        fi
    else
        if type feh > /dev/null 2>&1; then
            EXEC=feh
            TARGETFILE=$HOME/.config/feh/bg.jpg
            mkdir -p $HOME/.config/feh
            while true
            do
                random_pick $TARGETFILE
                $feh --bg-fill -z $TARGETFILE
                sleep $interval
            done
        fi
    fi
#fi

rm $_lockfile
```

## xmonad.hs

```haskell
main = do
  -- (中略)
  xmonad $ ewmh defaultConfig
    { -- (中略)
    , startupHook           = myStartupHook
    }


myStartupHook = do
    --spawn "feh --bg-fill -z ~/Pictures/wallpapers/*"
    spawn "/home/USER/bin/set-background.sh"
```

# kill processes

set background側で慣れない`trap`なんか使っちゃったりして．
でもってxmonadを落とす時，Mod3+Shift+Qで落とすわけで，その際にSIGQUITあたりが飛んでいるんだと思うんだが，
どうやら私の書いたスクリプトは落ちてくれないことが分かった．

具体的には，

- メインのプロセスはSIGKILLでないと落とせなかった
- xmonadのstartupHookで起動した場合，`/bin/sh -c`からの呼び出しと処理するプロセスの2プロセスが実行されるため，両方落とす必要がある

以上のことから，以下の実装とcrontabの設定を行なった．
現象さえ分かれば，実装には特筆することがないので，最終的なスクリプトと設定だけ記載する．

## kill-set-background.sh

```sh
#!/bin/sh

# to kill set-background.sh set this script in crontab

rm /tmp/set-background.sh.lock
pgrep -f set-background.sh | xargs kill -KILL
```

## crontab

```sh
crontab -l

#(略)
# m h dom mon dow  command
0 0 * * * /home/USER/bin/kill-set-background.sh
```