見出し画像

Mac を放置するとスタックチャンの顔が出てくるスクリーンセーバを作った

リポジトリ:

きっかけ

「画面を開いたまま放置すると、スタックちゃんの顔になる」——それだけのアイデアから始めた。

スタックちゃん (Stack-chan) は、瞬きしたり呼吸でゆらいだりする小さな卓上ロボット。あの顔がスリープ前の Mac にふっと出てきたら可愛いだろう、というだけの動機だった。スクリーンセーバを作ったことは一度もなかったので、ほぼ手探りでのスタートになった。

実際に動いている様子がこちら:


最初の方針:Electron で両対応

最初は Mac / Windows 両方で動かしたかったので、Electron の常駐アプリ方式で雛形を組んだ。powerMonitor.getSystemIdleTime() で無操作時間を監視し、一定時間で全画面ウィンドウを出し、何か入力があれば消す——という素朴な作り。

顔は本家 meganetaaan/m5stack-avatar (MIT) を参考にした。ただし本家はマイコン向けの C++ なので Web にそのままは載らない。そこで Canvas でプロシージャルに描き直すことにした(目・口を図形で描いて、瞬き・呼吸・視線のゆらぎを計算で動かす)。MIT なので、attribution さえ残せば「本家を参考に Web へ移植」という形で公開できる。

これで一度は動いた。瞬きも呼吸もする。けれど——

「これはスタックちゃんじゃないな」

出てきた顔を見て最初に思ったのがこれだった。パーツは同じでも、目のサイズ・位置・間隔が微妙に違う。"それっぽい近似" では本家には見えない。

そこで近似をやめ、本家のソースを読んで既定値をそのまま移植することにした。Face.cpp / Eye.cpp / Mouth.cpp / ColorPalette.cpp を読み、

・目 = 半径8の塗り円。右目 (90, 93) / 左目 (230, 96)、まばたきは一瞬だけ横線になる本家方式
・口 = Mouth(50, 90, 4, 60) の塗り矩形。待機時は幅90×高4の細いバー
・配色 = 背景黒 / 顔白(本家 ColorPalette の既定)
・仮想画面 320×240(M5 の画面サイズ)に本家座標で描き、ウィンドウ全体へ拡大

という具合に、座標も配色も本家の数値をそのまま使った。これでようやく「スタックちゃんの顔」になった。

Electron をやめて .saver にする

動くものができてくると欲が出る。.app 化して、ゆくゆくは配布もしたい。ここで調べていくうちに、Electron 常駐方式の弱さが見えてきた。常駐・オーバーレイ・入力監視はいずれも macOS のサンドボックスと相性が悪く、配布や App Store を考えると面倒が多い。

対して、Apple 公式の ScreenSaverView(.saver バンドル)は、

・アイドル判定・全画面化・復帰を OS がやってくれる
・常駐やオーバーレイの権限がいらない
・App Store 提出も視野に入る王道ルート

と、いいことづくめだった。そこで同じ顔を Swift の ScreenSaverView に移植することにした。Canvas 版で固めた座標とロジックがあるので、移植自体はスムーズだった。

没にしたアイデア

途中、いくつか欲張ったアイデアも出た。

・Mac のジャイロで顔が動く:MacBook の加速度センサ(落下検知用の SMS)を覗けないか
・叩くと音が鳴る
・環境光センサ(ALS)で明るさに反応:ioreg を漁ってみたが、.saver のサンドボックスからは取れず断念

このあたりは「.saver の中から、許可ダイアログを出さずに取れる情報」という制約に阻まれて没になった。逆に言うと、この制約が次の機能の設計を決めた。

状態に応じて表情が変わる

「重いときは怒り顔」みたいに、状況によって顔が変わると楽しい。しかもこれなら、許可ダイアログの出ない API だけで実装できる。

最終的にこうなった(上が優先):

・CPU 負荷が高い / 発熱 → 怒り(目の上を斜め三角でカット)。getloadavg / ProcessInfo.thermalState で取得
・充電中(電源接続)→ 嬉しい(◠ の目)。IOKit 電源情報
・バッテリー < 20% → 悲しい。IOKit 電源情報
・表示が長い(既定 5 分超)→ 眠い(半目)。表示経過時間
・それ以外 → 通常

マイク・カメラ・位置情報のようなプライバシー許可が必要な API は一切使っていないので、スクリーンセーバのまま反応できる。放置していると、Mac の機嫌がそのまま顔に出る。

ビルドは Xcode なし、CLI だけ

地味に気に入っているのがここ。Xcode を開かず swiftc だけで .saver を組み立てている。

build.sh が、

  1. arm64 + x86_64 で個別にコンパイル

  2. lipo でユニバーサルバイナリ化

  3. .saver バンドルとして組み立て

  4. ad-hoc 署名

までを自動でやってくれる。

(以下は note の「コード」ブロックに入れると読みやすいです)

cd macos-saver
./build.sh open # ビルド → ~/Library/Screen Savers へ導入 → 設定画面を開く

表情の確認も、アイドルを待たずにプレビューできるようにした:

./build.sh preview # n:通常 / h:嬉しい / a:怒り / d:悲しい / s:眠い / space:自動

ライセンスとクレジット

・本リポジトリ:MIT
・顔の挙動・表情ロジックは m5stack-avatar by @meganetaaan(Shinya Ishikawa, MIT, 2018)を参考に再実装。素晴らしい原作に感謝します
・「スタックちゃん / Stack-chan」の名称・キャラクターは @meganetaaan 氏の stack-chan に由来。本リポジトリは個人的な再実装であり、公式の製品ではありません

今後

・Windows 版(Electron ベースで開発中)
・配布版(Developer ID 署名 + notarization)
・App Store 版(.saver を同梱するマネージャアプリ)
・設定 UI(しきい値・色)


スクリーンセーバを作ったのは初めてだったけれど、「OS に任せられるところは任せる」と割り切った瞬間に一気に楽になった。放置した Mac がスタックちゃんの顔で、しかも疲れてたり眠そうにしてたりするのは、思っていたよりずっと愛着が湧く。

※zennに投稿したものを再利用しています。

いいなと思ったら応援しよう!