Sway bar上にshutdownボタンをおいてみる

# Shutdownボタンがほしい

Sway/i3を使い始めてだいぶ経って来ました。
コーディングしたりでキーボード叩くときはいいのですが、ブラウザで適当に動画眺めるだけのときは、shutdownのためにキーボードに持ち替えるのはめんどくさいなぁ、、という時があります。

マウスだけでshutdownさせるというのを目標に、やり方整理しました。
それっぽい形にはなりましたので、tipsをまとめようと思います。

# 実装方針

i3blocksの上に実装します。
もともとbarにはi3blocksを使っており、そのままそれを使うことにしています。
OSは、今回あまり重要ではないですが、Ubuntuです。

最終的にはこんな感じになります。

![20221229_18h59m45s_grim.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/587269/77e7d8d3-5ac7-60b4-3055-dcb738d816f8.png)

myupというのが、今回実装したshutdownボタンです。
私の都合で恐縮ですが、もともとshutdownはアップデートを実行してからshutdownするスクリプトを作り、それを呼び出す方法を取ってました。
そのscriptをmyupと命名していた関係で、shutdownボタンはmyupとなってます。

これを押すと、、、

![20221229_19h00m33s_grim.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/587269/46c16e4f-d448-f0c3-9d36-6945de16c588.png)

一回確認します。
間違ってクリックしてしまうのは全然ありうるので、それでshutdownされるのは流石にめんどいので。

Readyを押すとshutdown。
Cancelを押すと元の画面に戻ります。

# 実装詳細

## i3blocks側
i3blocksの該当箇所はこんなふうになります。

```
# Shutdown
[shutdown]
full_text=myup
interval=once
command=$LOCALBIN/myup_button
signal=12

[cancel_shutdown]
interval=once
command=$LOCALBIN/myup_button_cancel
signal=13
```

LOCALBINは、swayのコンフィグでi3blocksを呼び出しているときに渡している変数です。

```
bar {
    position bottom
    status_command SCRIPT_PATH=~/.config/i3blocks/i3blocks-contrib LOCALBIN=~/.local/bin i3blocks
    colors {
        statusline #ffffff
        background #323232
        inactive_workspace #32323200 #32323200 #5c5c5c
    }
```

実はshutdownとcancel_shutdownの２つの箱がバーの上に乗っているのですが、最初はcancel_shutdownにはなにも表示していないので、見えなくなってます。
commandの中身は、バーの該当箇所をクリックしたときと、signalを受け取ったときに呼び出されます。
クリックしたときに反応する実装は、違和感とかないでしょう。
一方、シグナルの方はなんで？となるかもです。
これはバー上の表示を変えるための小細工です。

最初にshutdownのmyupの文字列をクリックしたあと、shutdownの箱には"Ready?"、cancel_shutdownの箱には"Cancel"と表示します。
前者の方はshutdownのmyupの文字列をクリックした際の処理の中でecho "Ready?"とすればOKなのですが、問題はcancel_shutdown側。
cancel_shutdown側は、cancel_shutdown側のcommandでecho "Cancel"てしないといけません。

逆に、Ready?|Cancel|の表示になっているときに、Cancelを押したら、shutdown側の表示はもとに戻したいです。

ということで、片側の箱クリックされた際に、もう一方の箱の処理を呼び出すトリガを引く必要があるので、signalを使ってます。

ただし、これを実装すると、コマンド側としてはクリックされて呼び出されたのかシグナルで呼び出されたのかの区別がつかず、うまく処理ができません。
なので、別途フラグファイルを作成して、その有無で動きを切り分けます。
詳細は次の後の通り。

## 呼び出されるスクリプト側

myup_buttonはこちら。
root権限が必要なコマンド使うので、chown root:root、chmod 4555としてます。

```
#!/bin/bash -x

CHECK_FLAG=/tmp/myup_check_flag
SIG_TO_CANCEL=/tmp/myup_sig_to_cancel
SIG_TO_SHUTDOWN=/tmp/myup_sig_to_shutdown


if [ -e ${SIG_TO_SHUTDOWN} ]; then
  rm ${SIG_TO_SHUTDOWN}
  echo ""
  exit 0
fi

if [ -e ${CHECK_FLAG} ]; then
  export DEBIAN_FRONTEND=noninteractive
  apt update
  apt dist-upgrade -y
  shutdown -h now
else
  touch ${CHECK_FLAG}
  touch ${SIG_TO_CANCEL}
  echo "Ready?"
  pkill -SIGRTMIN+13 i3blocks
fi
```

myup_button_cancelはこちら。

```
#!/bin/bash

CHECK_FLAG=/tmp/myup_check_flag
SIG_TO_CANCEL=/tmp/myup_sig_to_cancel
SIG_TO_SHUTDOWN=/tmp/myup_sig_to_shutdown


if [ -e ${SIG_TO_CANCEL} ]; then
  rm ${SIG_TO_CANCEL}
  echo "Cancel"
  exit 0
fi

rm ${CHECK_FLAG}
touch ${SIG_TO_SHUTDOWN}
echo ""
pkill -SIGRTMIN+12 i3blocks
```

どちらも、最初の処理でSIG_TO_XXXのフラグの有無を見て、存在していた場合はecho(バー上表示の変更)だけして、処理を終えます。
それぞれの箱について、相方の箱がフラグを立てる→sigal投げる→スクリプトが呼び出される→フラグがあるからecho＋フラグ削除だけして終了、となります。
その後、箱をクリックしてスクリプトを呼び出したときは、フラグがないので、本来その箱でさせたい処理（後半の処理）を実施する、となります。

myup_button側は、初回クリックのときはshutdown確認用のフラグ（CHECK_FLAG)を立てつつ、相方にシグナル投げて終了（SIG_TO_CANCELも立ててます）。
２回目（CHECK_FLAGあり）の場合は、更新してから終了。

myup_button_cacnelは、クリックされたらCHECK_FLAGを消して、相方にシグナル。

フロー図書くとわかりやすいのですが、流石にめんどくさいのでご容赦を。

# まとめ

まぁそれっぽいものは作れました。
そんだけ。