bevy_live_wallpaper 0.5.0

A Bevy plugin to create live wallpapers.
Documentation
use bevy::{
    camera::RenderTarget,
    prelude::*,
    render::{
        Render, RenderApp, RenderSystems, extract_resource::ExtractResourcePlugin,
        render_resource::Extent3d,
    },
};

use crate::{
    LiveWallpaperCamera, WallpaperPointerState, WallpaperSurfaceInfo, WallpaperTargetMonitor,
};

use super::{
    X11AppState,
    render::{
        X11GpuSurfaceState, X11RenderTarget, X11SurfaceDescriptor, create_x11_image,
        prepare_x11_surface, present_x11_surface,
    },
};

#[derive(Default)]
pub(crate) struct X11BackendPlugin;

impl Plugin for X11BackendPlugin {
    fn build(&self, app: &mut App) {
        let target_monitor = app
            .world()
            .get_resource::<WallpaperTargetMonitor>()
            .copied()
            .unwrap_or_default();

        let (app_state, initial_config) = X11AppState::connect(target_monitor)
            .expect("failed to initialize X11 wallpaper backend");

        info!(
            "Connected to X11 wallpaper window: {}x{} (target: {:?})",
            initial_config.width, initial_config.height, target_monitor
        );

        let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
            return;
        };

        render_app
            .init_resource::<X11GpuSurfaceState>()
            .add_systems(
                Render,
                prepare_x11_surface.in_set(RenderSystems::PrepareResources),
            )
            .add_systems(Render, present_x11_surface.in_set(RenderSystems::Cleanup));

        let target_image = {
            let mut images = app.world_mut().resource_mut::<Assets<Image>>();
            create_x11_image(&mut images)
        };

        app.insert_resource(X11SurfaceDescriptor::new())
            .insert_resource(X11RenderTarget::new(target_image))
            .add_plugins((
                ExtractResourcePlugin::<X11SurfaceDescriptor>::default(),
                ExtractResourcePlugin::<X11RenderTarget>::default(),
            ))
            .add_systems(PostUpdate, x11_event_system)
            .add_systems(
                PostUpdate,
                (
                    sync_x11_render_target_image.after(x11_event_system),
                    assign_x11_camera_target.after(sync_x11_render_target_image),
                ),
            )
            .insert_non_send(app_state);
    }
}

fn x11_event_system(
    mut app_state: NonSendMut<X11AppState>,
    mut surface_descriptor: ResMut<X11SurfaceDescriptor>,
    target_monitor: Res<WallpaperTargetMonitor>,
    mut pointer_state: ResMut<WallpaperPointerState>,
    mut surface_info: ResMut<WallpaperSurfaceInfo>,
) {
    if !app_state.is_running() {
        return;
    }

    app_state.poll_events();

    if target_monitor.is_changed()
        && let Err(err) = app_state.apply_target(*target_monitor)
    {
        warn!("Failed to apply target monitor change: {err}");
    }

    if let Some((x, y, w, h)) = app_state.current_bounds() {
        surface_info.set(x, y, w, h);
    }

    if let Some(sample) = app_state.poll_pointer(pointer_state.last.as_ref()) {
        pointer_state.last = Some(sample);
    }

    if let Some(surface_config) = app_state.take_surface_config() {
        info!(
            "X11 surface configured: {}x{}",
            surface_config.width, surface_config.height
        );
        surface_descriptor.handles = Some(surface_config.handles);
        surface_descriptor.width = surface_config.width;
        surface_descriptor.height = surface_config.height;
        surface_descriptor.bump_generation();
    }
}

fn sync_x11_render_target_image(
    descriptor: Res<X11SurfaceDescriptor>,
    mut target: ResMut<X11RenderTarget>,
    mut images: ResMut<Assets<Image>>,
) {
    if descriptor.width == 0 || descriptor.height == 0 {
        return;
    }

    if target.last_applied_generation == descriptor.generation {
        return;
    }

    if let Some(mut image) = images.get_mut(&target.image) {
        let size = Extent3d {
            width: descriptor.width,
            height: descriptor.height,
            depth_or_array_layers: 1,
        };

        if image.texture_descriptor.size != size {
            image.texture_descriptor.size = size;
        }

        image.resize(size);
    }

    target.last_applied_generation = descriptor.generation;
}

fn assign_x11_camera_target(
    target: Res<X11RenderTarget>,
    mut commands: Commands,
    cameras: Query<Entity, With<LiveWallpaperCamera>>,
) {
    for entity in &cameras {
        commands
            .entity(entity)
            .insert(RenderTarget::Image(target.image.clone().into()));
    }
}