クリスマスなのでデスクトップ上に雪を降らせてみた[Mac、Tauri]


今日は12/25クリスマスですね。

みなさん良いクリスマスをお過ごしですか？

僕はクリぼっちです、はい。

今回はクリスマスといえば雪！ということでデスクトップ上に雪を降らせてみようと思います。

# 使用する技術

今回はTauriを使ってみようと思います。

Tauriとは、Rustで作成されたクロスプラットフォームGUIフレームワークです。

Tauriを使うことでWindows、macOS、Linux向けのデスクトップアプリをReactなどのweb技術で書くことができます。

https://tauri.app/

# Tauriのセットアップ


## インストール
https://v1.tauri.app/v1/guides/getting-started/setup/

tauriには、Bash、Powershell、npm、yarn、pnpm、deno、bun、Cargoなど多くのパッケージマネージャーを使うことができます。

今回はcargoでやってみようと思います。
create-tauri-app

```zsh
cargo install create-tauri-app --locked
```

create-tauri-appでtaruiプロジェクトを作成します。
```zsh
cargo create-tauri-app
```

すると諸々の設定が出てきます。
それを以下のように設定してみました。
```zsh
❯ cargo create-tauri-app
✔ Project name · christmas
✔ Identifier · com.christmas.app
✔ Choose which language to use for your frontend · TypeScript / JavaScript - (pnpm, yarn, npm, deno, bun)
✔ Choose your package manager · pnpm
✔ Choose your UI template · React - (https://react.dev/)
✔ Choose your UI flavor · TypeScript
```

tauriのCLIツールを依存関係に追加します。

```zsh
pnpm add -D @tauri-apps/cli@1
```


# バックエンド側(Rust)
ウィンドウがマウスのイベントを透過するようにする必要があったので、Tauriのウィンドウを制御している`src-tauri`をいじっていこうと思います。

Tauriでは`tauri.conf.json`でいくつか設定を行うことができます。

設定の種類は下から探すことができます。

https://v2.tauri.app/reference/config/#definitions

`src-tauri`に`tauri.conf.json`があるので、下のように書きました。

```tauri.conf.json
{
  "$schema": "https://schema.tauri.app/config/2",
  "productName": "christmas",
  "version": "0.1.0",
  "identifier": "com.christmas.app",
  "build": {
    "beforeDevCommand": "pnpm dev",
    "devUrl": "http://localhost:1420",
    "beforeBuildCommand": "pnpm build",
    "frontendDist": "../dist"
  },

  "app": {
    "windows": [
      {
        "maximized": true,
        "transparent": true,
        "alwaysOnTop": true,
        "decorations": false
       }
    ],
    "macOSPrivateApi": true,
    "security": {
      "csp": null
    }

  },
  "bundle": {
    "active": true,
    "targets": "all",
    "icon": [
      "icons/32x32.png",
      "icons/128x128.png",
      "icons/128x128@2x.png",
      "icons/icon.icns",
      "icons/icon.ico"
    ]
  }
}
```

ポイントは、`app`の`windows`にWindowConfigの設定を行うことがです。
この部分をいじることで画面の大きさや透明度やバーの表示などいろいろいじることができるようになります。

https://v2.tauri.app/reference/config/#windowconfig

設定した項目としては下のようになっています。

|項目|設定内容
|---|---
|maximized|画面を最大化
|transparent|画面を透明化
|alwaysOnTop|画面を再上面に
|decorations|上部のバーを消す


# フロント側(React)

フロント側はReactで書いている関係でReactのライブラリを使うことができるので、`react-snowfall`を使おうと思います。

https://github.com/cahilfoley/react-snowfall

依存関係に追加します。
```zsh:zsh
pnpm add react-snowfall
```

このライブラリをApp.tsxで使用します。

```typescript:App.tsx
import './App.css';
import {Snowfall} from "react-snowfall";

function App() {

    const snowflake1 = document.createElement('img')
    snowflake1.src = '/snowflake.png';

    const image = [snowflake1]

    return (
        <Snowfall
            color="#fff"
            images={image}
            style={{
                position: 'fixed',
                width: '100vw',
                height: '100vh',
            }}
            radius={[5,20]}
            snowflakeCount={200}
        />
    )
}

export default App;
```

注意としては、ルートにあるpublicディレクトリに画像を入れる必要があります。
viteの仕様でpublicの置いた画像は`/ファイル名`でアクセスできるようになります。


>snowflakeのgithubpagesで使われている画像を持ってきました。

# 使用した結果

こんな感じで雪を降らせながら後ろにあるターミナル操作をできるようにしました。
![snowflake.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3766230/994b667d-f894-2def-fa48-7f4cfd1cc152.gif)




# ビルドしてみる
ビルドは下のコマンドで行うことができます。

```zsh:zsh
pnpm tauri build
```

# アイコンを変えてみる
デフォルトだと、ビルドしたアプリケーションのアイコンはTauriのiconになってしまいます。

アイコンを変えるには、ルートディレクトリにアイコンにしたい画像を配置して、`app-icon.png`とします。


そこからtauriのicon生成コマンドがあるのでそれを打ちます。
```zsh:zsh
pnpm tauri icon
```

すると、さまざまなサイズのアイコンが`src-tauri/icons`の中に複製させれます。

```zsh:zsh
❯ pnpm tauri icon

> christmas@0.1.0 tauri /Users/maoz/Documents/application/christmas
> tauri "icon"

    Appx Creating StoreLogo.png
    Appx Creating Square30x30Logo.png
    Appx Creating Square44x44Logo.png
    Appx Creating Square71x71Logo.png
    Appx Creating Square89x89Logo.png
    Appx Creating Square107x107Logo.png
    Appx Creating Square142x142Logo.png
    Appx Creating Square150x150Logo.png
    Appx Creating Square284x284Logo.png
    Appx Creating Square310x310Logo.png
    ICNS Creating icon.icns
    ICO Creating icon.ico
    PNG Creating 32x32.png
    PNG Creating 128x128.png
    PNG Creating 128x128@2x.png
    PNG Creating icon.png
    PNG Creating mipmap-hdpi/ic_launcher_foreground.png
    PNG Creating mipmap-hdpi/ic_launcher_round.png
    PNG Creating mipmap-hdpi/ic_launcher.png
    PNG Creating mipmap-mdpi/ic_launcher_foreground.png
    PNG Creating mipmap-mdpi/ic_launcher_round.png
    PNG Creating mipmap-mdpi/ic_launcher.png
    PNG Creating mipmap-xhdpi/ic_launcher_foreground.png
    PNG Creating mipmap-xhdpi/ic_launcher_round.png
    PNG Creating mipmap-xhdpi/ic_launcher.png
    PNG Creating mipmap-xxhdpi/ic_launcher_foreground.png
    PNG Creating mipmap-xxhdpi/ic_launcher_round.png
    PNG Creating mipmap-xxhdpi/ic_launcher.png
    PNG Creating mipmap-xxxhdpi/ic_launcher_foreground.png
    PNG Creating mipmap-xxxhdpi/ic_launcher_round.png
    PNG Creating mipmap-xxxhdpi/ic_launcher.png
    iOS Creating AppIcon-20x20@2x-1.png
    iOS Creating AppIcon-20x20@1x.png
    iOS Creating AppIcon-20x20@2x.png
    iOS Creating AppIcon-20x20@3x.png
    iOS Creating AppIcon-29x29@2x-1.png
    iOS Creating AppIcon-29x29@1x.png
    iOS Creating AppIcon-29x29@2x.png
    iOS Creating AppIcon-29x29@3x.png
    iOS Creating AppIcon-40x40@2x-1.png
    iOS Creating AppIcon-40x40@1x.png
    iOS Creating AppIcon-40x40@2x.png
    iOS Creating AppIcon-40x40@3x.png
    iOS Creating AppIcon-60x60@2x.png
    iOS Creating AppIcon-60x60@3x.png
    iOS Creating AppIcon-76x76@1x.png
    iOS Creating AppIcon-76x76@2x.png
    iOS Creating AppIcon-83.5x83.5@2x.png
    iOS Creating AppIcon-512@2x.png
```

ここからビルドすると変更したいアプリのアイコンになります。

# CIで自動ビルドさせる

GithubActionsを使って自動ビルドしてみようと思います。

https://github.com/tauri-apps/tauri-action

`.github/workflows/build.yml`に下のように記述します。

```.github/workflows/build.yml
name: 'build'

on:
  push:
    branches:
      - main

jobs:
  publish-tauri:
    permissions:
      contents: write
    strategy:
      fail-fast: false
      matrix:
        include:
          - platform: 'macos-latest' # for Arm based macs (M1 and above).
            args: '--target aarch64-apple-darwin'

    runs-on: ${{ matrix.platform }}
    steps:
      - uses: actions/checkout@v4

      - name: setup node
        uses: actions/setup-node@v4
        with:
          node-version: lts/*

      - name: setup pnpm
        uses: pnpm/action-setup@v4
        with:
          version: 9

      - name: install Rust stable
        uses: dtolnay/rust-toolchain@stable
        with:
          toolchain: stable

      - name: install frontend dependencies
        run: pnpm install

      - uses: tauri-apps/tauri-action@v0.5.18
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          tagName: app-v__VERSION__
          releaseName: 'App v__VERSION__'
          releaseBody: 'See the assets to download this version and install.'
          releaseDraft: false
          prerelease: false
          args: ${{ matrix.args }}
```
mainブランチにpush(or PRでマージされた時)にactionsが走るようにしています。

またmatrixを使うことでos毎にビルドの仕方を変えるようにしています。

stepsで行ってることは以下の通りです。
|step|内容
|---|---
|actions/checkout@v4|actions内にリポジトリをクローンしています
|actions/setup-node@v4|actions内にnodeをセットアップしています
|pnpm/action-setup@v4|pnpmをセットアップしています
|dtolnay/rust-toolchain@stable|安定ver(stable)のrustの環境をセットアップしています。
|pnpm install|pnpmで依存関係をインストールしています
|tauri-apps/tauri-action@v0.5.18|自動ビルドとreleaseに自動的にあげるようにしています


これでmainにpushされるとreleasesページに作成されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3766230/aa996be3-544d-37d7-be5b-4b21429daaba.png)


しかしそのまま実行しようとすると下のような表示が出ます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3766230/8e14db50-057f-5eb2-9828-5ebb674689c6.png)

これはおそらくですが、公証というアプリケーションに対して安全であるという署名がないからだと考えられます。

なのでxattrコマンドで許可する必要があります。
>自己責任でお願いします
```
xattr -rc /Applicaions/christmas.app
```


# おまけ
setIgnoreMouseEventのやり方を調べる過程でしたのissueを参考にしようとしました。

https://github.com/tauri-apps/tao/issues/184

しかし、ここに書いてあるのは2年前の文法でTauriはv2.0で変わったばっかりでここに書かれているコードでは動かすことができませんでした。

その際にGPTとかドキュメントとかを読んで動いたコードは下のようになりました。

```rust:lib.rs
use tauri::Manager;
#[cfg(target_os = "macos")]
use cocoa::appkit::NSWindow;
pub fn run() {

    tauri::Builder::default()
        .setup(|app| {
            let window = app.get_webview_window("main").unwrap();
    
            #[cfg(target_os = "macos")]
            unsafe {
                let ns_window = window.ns_window().unwrap() as cocoa::base::id;
                // マウスイベントを透過させる
                ns_window.setIgnoresMouseEvents_(cocoa::base::YES);
            }
    
            window.set_always_on_top(true)?;
            window.set_decorations(false)?;
            Ok(())
        })
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}

```

`use cocoa::appkit::NSWindow`は、macOSのアプリケーションで使われるappKitのNSWindowクラスを呼び出しています。

TauriはRustでOSのAPIを叩くことができます。それを利用して上記のようにAPIを無理やり叩いていました。

ただ、ver2.0から標準設定ができるようになりよしなに叩くAPIを切り替えたりしてくれるようになったぽいです。

さすがTauriですね。

# リポジトリ

リポジトリは下にあります。
よかったらクローンして遊んでみてください!

https://github.com/maooz4426/christmas