コンテンツにスキップ

クイックスタート

このページは、はじめから終わりまでの流れをひととおり示します。その背景にある考え 方は、先に拡張機能をつくるを読んでください。

1. SDK と非同期ランタイムに依存する

Section titled “1. SDK と非同期ランタイムに依存する”

拡張機能に必要なものは 2 つです。SDK のクレートと、非同期のコードを動かす何か。そ れ以外はありません。

[dependencies]
vros-ext = "2.0"
tokio = { version = "1", features = ["rt-multi-thread", "macros"] }

クレートのバージョンは vrOS 自身のバージョンで、プロトコルのバージョンではありませ ん。通信のバージョンは別の定数 vxp::VXP_VERSION で、現在は 1.7 です。ホストは 現行の VXP メジャーバージョンと、その 1 つ前を受け付け、1.x のマイナーは追加しかし ません。古い SDK で作った拡張機能も新しい vrOS で動き続け、それ以降に加わったもの が見えないだけです。

vrOS は、エンドポイント、起動ごとのトークン、拡張機能の id を環境変数に入れてバイ ナリを起動します。connect_from_env はそれらを読んで接続し、起動時の競合をしのぐ ために数秒のあいだ再試行します。

そのうえで hello を送ります。猶予は起動から 5 秒で、それまで他には何も応答されませ ん。先に hello、初期化はそのあとです。

use vros_ext::conn::{Client, SocketConnection};
use vros_ext::event::VxpEvent;
use vros_ext::hello::{HelloReply, HelloRequest};
use vros_ext::vxp;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let conn = SocketConnection::connect_from_env().await?;
let client = Client::new(conn);
let reply = client
.hello(&HelloRequest {
id: std::env::var(vxp::ENV_EXT_ID)?,
vxp_version: vxp::VXP_VERSION.into(),
token: std::env::var(vxp::ENV_TOKEN).ok(),
})
.await?;
// The welcome carries what the user actually granted, and their locale.
let (granted, locale) = match reply {
HelloReply::Welcome { granted, locale, .. } => (granted, locale),
HelloReply::Rejected { code, message } => {
return Err(format!("hello rejected ({code:?}): {message}").into());
}
other => return Err(format!("unknown hello reply: {other:?}").into()),
};
// Do your setup here — read KV, subscribe to what you were granted,
// push your panel's words. Then park on the event stream.
let mut events = client.take_events().ok_or("no event stream")?;
while let Some(event) = events.recv().await {
match event {
VxpEvent::ShuttingDown => break,
_ => {}
}
}
Ok(())
}

ケーパビリティを使う前に、マニフェストがまるごと受け入れられたと決めてかからず、 granted を確認してください。利用者の同意はケーパビリティごとで、許可されなかった ものはそのセッションのあいだ変わりません。

vros-ext.toml はバイナリの隣に置かれ、その拡張機能を説明します。同意画面が読むの はこれで、読まれるのは拡張機能が停止しているあいだです。

[extension]
id = "com.example.hello"
name = "Hello"
version = "0.1.0"
author = "Your name"
entry = "hello.exe" # the binary name; drop the .exe on Linux
restart = "on-crash"
[capabilities]
bus = ["vr.lifecycle"]
[panels]
overlay = "ui/hello.slint"

id は [a-z0-9.-] から取った小文字の逆引き DNS 形式で、慣例は tld.author.name です。これはインストール先のフォルダー名でもあり、KV の名前空間でもあり、バスのプ レフィックス(ext.<id>.*)でもあります。一度決めたら、そのまま使い続けてくださ い。

インストールとは、マニフェストと、そこに名前の書かれたバイナリと、パネルのソース を入れたフォルダーのことです。

%LOCALAPPDATA%\vrOS\extensions\com.example.hello\ (Windows)
~/.local/share/vros/extensions/com.example.hello/ (Linux)
├─ vros-ext.toml
├─ hello.exe
└─ ui/hello.slint

設定 → 拡張機能を開き、「更新」を押します。その拡張機能が、要求したケーパビリティ を並べた状態で、無効として現れます。有効にして同意すると、vrOS がそれを起動しま す。同じページに状態、再起動の回数、直近のエラーが出ます。あとで削除するのもここ です。

プロトコルリファレンスは、通信上のすべての名前空間 — KV とシークレット、バス、パネルのプロパティと画像、コマンド、設定、文字列、オーディ オ、メディア、フェイストラッキング — を、上限とエラーコードとともに扱います。

  • hello は形式ではなく締め切りです。 5 秒を過ぎるとホストはプロセスを終了さ せ、指定された再起動ポリシーを適用します。
  • パネルのパスはパース時に検査されます。 相対パス、区切りはスラッシュ、各要素 は英字・数字・ドット・アンダースコア・ハイフンのみ、末尾は .slint。ディレクト リの遡り、絶対パス、ドライブレター、バックスラッシュは、ディスクを読む前に拒否 されます。
  • マニフェストのフィールドはホストのバージョンを前提にします。 未知のマニフェ ストのフィールドは、受け流されるのではなくエラーになります。[[settings]] を含む マニフェストは VXP 1.1 より古いホストで、number や select の設定は VXP 1.3 よ り古いホストで、rail_label や icon はそのフィールドを知らないホストでパースに 失敗します。例外はオーバーレイの width_mm だけで、古いホストはこれを無視し、既 定の幅でパネルを表示します。
  • 1 行につき JSON オブジェクトは 1 つです。 バッチの配列は拒否され、3 MiB を超 えるフレームは破損として扱われます。
  • 話すバージョンを正直に伝えてください。 hello には決め打ちの文字列ではなく vxp::VXP_VERSION を送ります。フェイスの更新は 1.5 以上、メディアの状態イベン トは 1.4 以上で hello を送った拡張機能にだけ届きます。
  • 永続する状態は KV だけです。 何をしている最中でも再起動されうると考え、残りは 組み立て直してください。

SDK のクレートは crates.io になく、それを収めたリポジトリ — クレート、リファレンス の hello 拡張機能、@vros/theme.slint モジュール、オーサリング契約 — は現時点では 非公開です。コードは MIT または Apache-2.0 の選択制でライセンスされており、公開さ れた瞬間に、このページからクレートとサンプルへリンクします。それより前にこれを相 手に開発したい場合は、Discord で声をかけてくださ い。