🖼️

動く壁紙を作ろう

に公開

はじめに

今回は、Rust製のゲームエンジンBevyでライブ壁紙を簡単に作成できるプラグイン、bevy_live_wallpaperを開発したので、その紹介と解説をします。

demo gif

flow-clock demo gif

リポジトリはこちらです。

https://github.com/yadokani389/bevy_live_wallpaper

このプラグインを使うと、Windows, Linux(Wayland/X11)のデスクトップ背景にBevyのシーンを直接描画できます。
この記事では、その使い方や仕組み、開発で工夫した点について詳しく解説していきます。

特徴

  • クロスプラットフォーム対応: Windows, Wayland, X11で動作します。
  • Bevyとの簡単な統合: Bevyアプリに数行追加するだけで導入できます。
  • インタラクティヴィティ: ゲームエンジンを利用しているのでマウスやその他の情報から動的に画面を変更できます。

使い方

1. 依存関係の追加

Cargo.tomlにお使いのプラットフォームに合わせた設定を追加します。

# Windows
cargo add bevy_live_wallpaper

# Linux
cargo add bevy_live_wallpaper -F=wayland,x11

2. プラグインのセットアップ

main.rsでプラグインをセットアップします。プラットフォームごとにウィンドウの設定を少し変更する必要があります。

src/main.rs
use bevy::prelude::*;
use bevy_live_wallpaper::{LiveWallpaperCamera, LiveWallpaperPlugin};

fn main() {
    let mut app = App::new();

    // プラットフォームごとにウィンドウ設定を調整
    let mut window_plugin = WindowPlugin::default();

    // Wayland/X11ではプライマリウィンドウを無効化
    #[cfg(any(feature = "wayland", feature = "x11"))]
    {
        window_plugin.primary_window = None;
        window_plugin.exit_condition = bevy::window::ExitCondition::DontExit;
    }

    // Windowsではボーダーレスウィンドウにする
    #[cfg(target_os = "windows")]
    {
        window_plugin.primary_window = Some(Window {
            decorations: false,
            ..default()
        });
    }

    app.add_plugins(DefaultPlugins.set(window_plugin));

    // プラグインを追加
    app.add_plugins(LiveWallpaperPlugin::default());

    app.add_systems(Startup, setup_scene).run();
}

fn setup_scene(mut commands: Commands) {
    // 壁紙として表示したいカメラに`LiveWallpaperCamera`コンポーネントを追加
    commands.spawn((Camera2d, LiveWallpaperCamera));

    commands.spawn((
        Sprite::from_color(Color::srgb(0.15, 0.4, 0.85), Vec2::splat(1600.0)),
        Transform::from_xyz(0.0, 0.0, 0.0),
    ));
}
  • Wayland/X11:
    Bevyがデフォルトで作成するウィンドウは不要なため、primary_window = Noneに設定します。プラグインが専用のウィンドウ(サーフェス)を作成します。
  • Windows:
    通常のウィンドウを作成し、プラグインがそれを壁紙にします。decorations: falseでウィンドウの枠を消しています。
  • LiveWallpaperCameraを付けたカメラの映像が壁紙として描画されます。

アーキテクチャ・仕組み

それぞれについて軽く説明します。

Windows

Win32 APIを直接利用して、Bevyウィンドウをデスクトップの背景レイヤーに統合します。
具体的には、ProgmanまたはWorkerWという特殊なウィンドウを探し出し、Bevyのウィンドウをその子ウィンドウとして設定します。
これにより、Bevyのシーンがデスクトップアイコンの背後に描画されるようになります。
ウィンドウのスタイルを調整して、ボーダーやタイトルバーを非表示にする必要があります。

Wayland

Waylandプロトコルを利用して、コンポジタと直接通信します。
wlr-layer-shell拡張プロトコルを使い、Bevyのシーンを描画するためのサーフェス(ウィンドウに相当)をデスクトップの最下層レイヤー (Layer::Bottom) に配置します。これにより、壁紙として機能させます。
Bevyが標準で作成するウィンドウは使わず、このプラグインが各モニターに対応した専用のWaylandサーフェスを管理します。
レンダリングはオフスクリーンイメージに対して行われ、その結果が各サーフェスにコピーされます。

X11

ルートウィンドウ(デスクトップの最背後にあるウィンドウ)の子として新しいウィンドウを作成します。
このウィンドウをStackMode::BELOWに設定することで、他のすべてのウィンドウの背面に配置し、壁紙のように見せかけます。
RandR (X Resize and Rotate) 拡張機能を使い、モニターの構成や解像度を取得します。
レンダリングはWaylandと同様にオフスクリーンで行われ、その結果が作成したX11ウィンドウに描画されます。

工夫した点

  • マウス対応
    せっかくゲームエンジンを壁紙にするプラグインなのだからインタラクトでき動的な動きができると嬉しいなと思いました。
    なのでマウス位置とキー入力を取得できるようにしています。
    どのプラットフォームでも直接ウィンドウにさわれないので、それぞれ別の方法で取得しています。
    Waylandのみ制約上背景の上にカーソルがないと取得できませんでしたが、統一されたAPIでマウス情報を扱えるようになっています。

  • ウィンドウモード
    デバッグしたいときなどにUIを触れない/表示されないと困るので(困ったので)、普通にウィンドウとして起動するモードがあります。
    プラグインを切るだけでいいかと思いきやカーソル位置などの情報が受け取れなくなるので引き続き同じAPIで取得できるようにしました。

macOS対応について

macOSは実現可能性がなかったわけではなく、シンプルに私がmacOSを持っていなかったので開発ができなかっただけです。
もし開発に協力してくださる方がいらっしゃればPRお待ちしております。

まとめ

Rustを使って動く壁紙を作ってみたいと思っている方、是非この機会にBevyに入門して最高の壁紙を作りましょう!

バグ報告や機能リクエストなど、GitHubでのフィードバックをお待ちしています。

Discussion