2022年3月23日水曜日

Tauri アプリ入門 3 - メインプロセスからレンダープロセスへ通信

Vanilla.js のプロジェクトを作って、「メインプロセス -> レンダープロセス」の通信を実装するところまで。

この通信のために、イベントという仕組みを利用するため、ついでに「レンダープロセス -> レンダープロセス」のイベント通信も試す。

今回は、プロジェクト作成直後から、必要な修正差分を示す形で進める。

前提

  • OS: Windows 11 Pro 21H2 ビルド 22000.556
  • Tauri の開発環境構が築済みであること
  • Vanilla.js の Tauri プロジェクトひな形が作成済みであること
    • 今回は、 tauri-app-event という名前でプロジェクトを作成した

その他、Tauri シリーズを読んでおくとよいかも。

メインプロセスからレンダープロセスへイベントを送信する

Tauri でメインプロセスからレンダープロセスへの通信を行い際には、イベントという仕組みを利用する。

今回は、以下順番で実装していく。

  1. Vanilla.js のための設定
  2. メインプロセス側にイベントを送信する関数を定義
    • イベント送信処理起動コマンドの実装
    • コマンドの登録

Vanilla.js のための設定

tauri.conf.json"withGlobalTauri": true を追加する。

diff --git a/tauri/tauri-app-event/src-tauri/tauri.conf.json b/tauri/tauri-app-event/src-tauri/tauri.conf.json
index e5699f4..8f75c29 100644
--- a/tauri/tauri-app-event/src-tauri/tauri.conf.json
+++ b/tauri/tauri-app-event/src-tauri/tauri.conf.json
@@ -7,7 +7,8 @@
     "distDir": "../dist",
     "devPath": "../dist",
     "beforeDevCommand": "",
-    "beforeBuildCommand": ""
+    "beforeBuildCommand": "",
+    "withGlobalTauri": true
   },
   "tauri": {
     "bundle": {

メインプロセス側にイベントを送信する関数を定義

イベント送信処理起動コマンドの実装

Rust 側で、 AppHandle#emit_all を実行することで、レンダープロセス側にイベントを通知できる。

第一引数にイベント名、第二引数に送信するデータを格納する。

以下では backendHello という名前で、構造体 BackendHelloEventPayload の構造をしたデータを送信している。

レンダープロセスで、イベントを listen する際に、イベント名を指定することで、同名のイベントを受け取れる。

diff --git a/tauri/tauri-app-event/src-tauri/src/command.rs b/tauri/tauri-app-event/src-tauri/src/command.rs
new file mode 100644
index 0000000..f1983a7
--- /dev/null
+++ b/tauri/tauri-app-event/src-tauri/src/command.rs
@@ -0,0 +1,42 @@
+use std::sync::mpsc::{self, TryRecvError};
+use std::thread;
+use std::time::Duration;
+
+use tauri;
+use tauri::Manager;
+
+// レンダープロセスへ送信するデータの構造を定義した構造体
+#[derive(Clone, serde::Serialize, serde::Deserialize)]
+pub struct BackendHelloEventPayload {
+  pub message: String,
+}
+
+// 初期化用関数
+// JS のイベントリスナー `DOMContentLoaded` から、初期化時に呼び出されることを想定
+#[tauri::command]
+pub fn post_setup_process_for_backend(app: tauri::AppHandle) {
+
+  let (tx, rx) = mpsc::channel();
+
+  // 新しいスレッドを作成して、 1 秒ごとにイベントを emit する処理を実装
+  tauri::async_runtime::spawn( async move {
+    loop {
+      println!("send backendHello event.");
+      thread::sleep(Duration::from_millis(1000));
+
+      // レンダープロセスへイベント送信
+      //     イベント名: backendHello
+      //     送信データ: { message: "Hello, World from backend!!!" }
+      app.emit_all("backendHello", BackendHelloEventPayload { message: "Hello, World from backend!!!".into() }).unwrap();
+
+      // emit 終了通知用チャンネルに通知が来たらループをストップ
+      match rx.try_recv() {
+          Ok(_) | Err(TryRecvError::Disconnected) => {
+              println!("stop backendHello event.");
+              break;
+          }
+          Err(TryRecvError::Empty) => {}
+      }
+    }
+  });
+
+  // 10 秒後に emit を終了(emit 終了通知用チャンネル経由でストップを通知)
+  tauri::async_runtime::spawn( async move {
+    thread::sleep(Duration::from_millis(10000));
+    let _ = tx.send(());
+  });
+}
+

コマンドの登録

先ほど作成したコマンドを、 invoke_handler で登録。

diff --git a/tauri/tauri-app-event/src-tauri/src/main.rs b/tauri/tauri-app-event/src-tauri/src/main.rs
index e994ea4..2dd091a 100644
--- a/tauri/tauri-app-event/src-tauri/src/main.rs
+++ b/tauri/tauri-app-event/src-tauri/src/main.rs
@@ -3,8 +3,13 @@
   windows_subsystem = "windows"
 )]

+mod command;
+
 fn main() {
   tauri::Builder::default()
+    // setup 中ではできない初期化処理を実行するコマンドを登録
+    // setup 中に spawn ができないので、それを行う
+    .invoke_handler(tauri::generate_handler![command::post_setup_process_for_backend])
     .run(tauri::generate_context!())
     .expect("error while running tauri application");
 }

レンダープロセス側にイベント受信処理を追加

window.__TAURI__.event.listen 関数を使って、イベント受信時に実行する関数を設定する。

diff --git a/tauri/tauri-app-event/dist/index.html b/tauri/tauri-app-event/dist/index.html
index 8da7e6c..507bb5e 100644
--- a/tauri/tauri-app-event/dist/index.html
+++ b/tauri/tauri-app-event/dist/index.html
@@ -10,11 +10,42 @@
     }

     body {
-      display: flex;
       align-items: center;
       justify-content: center;
     }
   </style>
+  <script type="text/javascript">
+    document.addEventListener('DOMContentLoaded', function() {
+        const invoke = window.__TAURI__.invoke;
+        const listen = window.__TAURI__.event.listen;
+
+        async function init() {
+            // バックエンドの初期化処理呼び出し
+            await invoke('post_setup_process_for_backend', {});
+
+            // バックエンドが emit するイベントの listen
+            //     イベント名: backendHello
+            //     受信データ: { message: "Hello, World from backend!!!" }(`event.payload` に格納されている)
+            const unlistenBackendHello = await listen('backendHello', event => {
+                console.log(event);
+                const p = document.createElement('p');
+                p.textContent = event.payload.message;
+                document.body.append(p);
+            });
+
+            // 5 秒後に listen を止める
+            setTimeout(() => {
+                console.log("timeout.");
+                unlistenBackendHello();
+                const p = document.createElement('p');
+                p.textContent = 'stop event listen.';
+                document.body.append(p);
+            }, 5000);
+        }
+
+        init();
+
+    });
+  </script>
   <body>
     <h1>tauri-app-event</h1>
   </body>

ここまで追加したのち、 npx tauri dev コマンドで実行すると、以下の挙動が確認できる。

  1. Rust 側で、 10 秒間、 1 秒ごとにイベントを発行
  2. JS 側で、起動から 5 秒間イベントを listen し、それ以後無視する

コメントが無いなど、多少差分があるが、前述の方針で実装したものを以下に格納しているので、必要に応じて確認してください。

参考資料

0 件のコメント:

コメントを投稿