From e6b4bc43f2c981f41ffaf69a87ad701489078200 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Moon=20Dav=C3=A9?= Date: Fri, 16 Jan 2026 21:52:07 -0500 Subject: [PATCH 1/8] Turn on file_watcher feature for Bevy --- Cargo.lock | 170 ++++++++++++++++++++++++++++++++++++++++++++++++++++- Cargo.toml | 2 +- 2 files changed, 170 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5e14b7b..3c3289f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -600,6 +600,7 @@ dependencies = [ "futures-lite", "futures-util", "js-sys", + "notify-debouncer-full", "ron", "serde", "stackfuture", @@ -2516,6 +2517,15 @@ dependencies = [ "simd-adler32", ] +[[package]] +name = "file-id" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1fc6a637b6dc58414714eddd9170ff187ecb0933d4c7024d1abbd23a3cc26e9" +dependencies = [ + "windows-sys 0.60.2", +] + [[package]] name = "find-msvc-tools" version = "0.1.5" @@ -2621,6 +2631,15 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" +[[package]] +name = "fsevent-sys" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2" +dependencies = [ + "libc", +] + [[package]] name = "futures-channel" version = "0.3.31" @@ -3229,6 +3248,26 @@ version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" +[[package]] +name = "kqueue" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac30106d7dce88daf4a3fcb4879ea939476d5074a9b7ddd0fb97fa4bed5596a" +dependencies = [ + "kqueue-sys", + "libc", +] + +[[package]] +name = "kqueue-sys" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b" +dependencies = [ + "bitflags 1.3.2", + "libc", +] + [[package]] name = "ktx2" version = "0.4.0" @@ -3506,6 +3545,18 @@ dependencies = [ "simd-adler32", ] +[[package]] +name = "mio" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys 0.61.2", +] + [[package]] name = "moxcms" version = "0.7.10" @@ -3662,6 +3713,43 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" +[[package]] +name = "notify" +version = "8.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d3d07927151ff8575b7087f245456e549fea62edf0ec4e565a5ee50c8402bc3" +dependencies = [ + "bitflags 2.10.0", + "fsevent-sys", + "inotify", + "kqueue", + "libc", + "log", + "mio", + "notify-types", + "walkdir", + "windows-sys 0.60.2", +] + +[[package]] +name = "notify-debouncer-full" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "375bd3a138be7bfeff3480e4a623df4cbfb55b79df617c055cd810ba466fa078" +dependencies = [ + "file-id", + "log", + "notify", + "notify-types", + "walkdir", +] + +[[package]] +name = "notify-types" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e0826a989adedc2a244799e823aece04662b66609d96af8dff7ac6df9a8925d" + [[package]] name = "ntapi" version = "0.4.2" @@ -5612,6 +5700,12 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + [[package]] name = "wasip2" version = "1.0.1+wasi-0.2.4" @@ -6221,6 +6315,15 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.5", +] + [[package]] name = "windows-sys" version = "0.61.2" @@ -6254,13 +6357,30 @@ dependencies = [ "windows_aarch64_gnullvm 0.52.6", "windows_aarch64_msvc 0.52.6", "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm", + "windows_i686_gnullvm 0.52.6", "windows_i686_msvc 0.52.6", "windows_x86_64_gnu 0.52.6", "windows_x86_64_gnullvm 0.52.6", "windows_x86_64_msvc 0.52.6", ] +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link 0.2.1", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", +] + [[package]] name = "windows-threading" version = "0.1.0" @@ -6282,6 +6402,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + [[package]] name = "windows_aarch64_msvc" version = "0.42.2" @@ -6294,6 +6420,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + [[package]] name = "windows_i686_gnu" version = "0.42.2" @@ -6306,12 +6438,24 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + [[package]] name = "windows_i686_msvc" version = "0.42.2" @@ -6324,6 +6468,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + [[package]] name = "windows_x86_64_gnu" version = "0.42.2" @@ -6336,6 +6486,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" @@ -6348,6 +6504,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + [[package]] name = "windows_x86_64_msvc" version = "0.42.2" @@ -6360,6 +6522,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + [[package]] name = "winit" version = "0.30.12" diff --git a/Cargo.toml b/Cargo.toml index bf30fe3..48019c7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,7 +21,7 @@ type_complexity = "allow" too_many_arguments = "allow" [workspace.dependencies] -bevy = { git = "https://github.com/bevyengine/bevy", branch = "main" } +bevy = { git = "https://github.com/bevyengine/bevy", branch = "main", features = ["file_watcher"] } processing = { path = "." } processing_pyo3 = { path = "crates/processing_pyo3" } processing_render = { path = "crates/processing_render" } From ecd8dea2669d29decc360fc4ff964b70152f773d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Moon=20Dav=C3=A9?= Date: Sat, 17 Jan 2026 16:51:56 -0500 Subject: [PATCH 2/8] Add Sketch asset, and a LivecodePlugin --- crates/processing_render/src/sketch.rs | 76 ++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 crates/processing_render/src/sketch.rs diff --git a/crates/processing_render/src/sketch.rs b/crates/processing_render/src/sketch.rs new file mode 100644 index 0000000..0bc59c7 --- /dev/null +++ b/crates/processing_render/src/sketch.rs @@ -0,0 +1,76 @@ +//! A Sketch asset represents a source file containing user code for a Processing sketch. +//! +//! Sketches are loaded through Bevy's asset system, which provides automatic file watching +//! and change detection. This enables hot-reloading workflows where artists can edit their +//! sketch code and see changes reflected immediately without restarting. +//! +//! This module is intentionally language-agnostic — it only handles loading source text from +//! disk. Language-specific crates (like `processing_pyo3`) are responsible for executing the +//! source and binding it to the Processing API. + +use bevy::{ + asset::{AssetLoader, LoadContext, io::Reader}, + prelude::*, +}; +use std::path::PathBuf; + +/// Plugin that registers the Sketch asset type and its loader. +pub struct LivecodePlugin; + +impl Plugin for LivecodePlugin { + fn build(&self, app: &mut App) { + app.init_asset::() + .init_asset_loader::(); + } +} + +/// A sketch source file loaded as a Bevy asset. +/// +/// The `Sketch` asset contains the raw source code as a string. It does not interpret +/// or execute the code — that responsibility belongs to language-specific crates. +#[derive(Asset, TypePath, Debug)] +pub struct Sketch { + /// The source code contents of the sketch file. + pub source: String, + + /// The original file path. + pub path: PathBuf, +} + +/// Loads sketch files from disk. +/// +/// Currently supports `.py` files, but the loader is designed to be extended +/// for other languages in the future. +#[derive(Default)] +pub struct SketchLoader; + +impl AssetLoader for SketchLoader { + type Asset = Sketch; + type Settings = (); + type Error = std::io::Error; + + async fn load( + &self, + reader: &mut dyn Reader, + _settings: &Self::Settings, + load_context: &mut LoadContext<'_>, + ) -> Result { + let mut source = String::new(); + + let mut bytes = Vec::new(); + reader.read_to_end(&mut bytes).await?; + + if let Ok(utf8) = str::from_utf8(&bytes) { + source = utf8.to_string(); + } + + let asset_path = load_context.path(); + let path: PathBuf = asset_path.path().to_path_buf(); + + Ok(Sketch { source, path }) + } + + fn extensions(&self) -> &[&str] { + &["py"] + } +} From 9d0947e4a938aa57895bced1b4d80f849fe2c37a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Moon=20Dav=C3=A9?= Date: Tue, 20 Jan 2026 08:53:19 -0500 Subject: [PATCH 3/8] Add SketchRootPath ConfigKey --- crates/processing_pyo3/src/graphics.rs | 5 +++++ crates/processing_render/src/config.rs | 1 + crates/processing_render/src/lib.rs | 10 +++++++++ crates/processing_render/src/sketch.rs | 28 ++++++++++++++++---------- 4 files changed, 33 insertions(+), 11 deletions(-) diff --git a/crates/processing_pyo3/src/graphics.rs b/crates/processing_pyo3/src/graphics.rs index 5f7be05..a17e449 100644 --- a/crates/processing_pyo3/src/graphics.rs +++ b/crates/processing_pyo3/src/graphics.rs @@ -121,6 +121,11 @@ impl Graphics { let mut config = Config::new(); config.set(ConfigKey::AssetRootPath, asset_path.to_string()); + config.set( + // TODO: this needs to be handed to us by python + ConfigKey::SketchRootPath, + "/home/moon/Code/libprocessing/crates/processing_pyo3/examples".to_string(), + ); init(config).map_err(|e| PyRuntimeError::new_err(format!("{e}")))?; let surface = glfw_ctx diff --git a/crates/processing_render/src/config.rs b/crates/processing_render/src/config.rs index 75766a1..81ba5aa 100644 --- a/crates/processing_render/src/config.rs +++ b/crates/processing_render/src/config.rs @@ -8,6 +8,7 @@ use std::collections::HashMap; #[derive(Clone, Hash, Eq, PartialEq)] pub enum ConfigKey { AssetRootPath, + SketchRootPath, } // TODO: Consider Box instead of String diff --git a/crates/processing_render/src/lib.rs b/crates/processing_render/src/lib.rs index 6f60575..f9d166c 100644 --- a/crates/processing_render/src/lib.rs +++ b/crates/processing_render/src/lib.rs @@ -4,6 +4,7 @@ pub mod geometry; mod graphics; pub mod image; pub mod render; +pub mod sketch; mod surface; use std::{cell::RefCell, num::NonZero, path::PathBuf, sync::OnceLock}; @@ -242,6 +243,15 @@ fn create_app(config: Config) -> App { } app.add_plugins(plugins); + if let Some(sketch_path) = config.get(ConfigKey::SketchRootPath) { + println!("DEBUG SKETCH PATH = {sketch_path}"); + app.register_asset_source( + "sketch_directory", + AssetSourceBuilder::platform_default(sketch_path, None), + ) + .add_plugins(sketch::LivecodePlugin); + } + app.add_plugins(( ImagePlugin, GraphicsPlugin, diff --git a/crates/processing_render/src/sketch.rs b/crates/processing_render/src/sketch.rs index 0bc59c7..4da828f 100644 --- a/crates/processing_render/src/sketch.rs +++ b/crates/processing_render/src/sketch.rs @@ -1,18 +1,13 @@ //! A Sketch asset represents a source file containing user code for a Processing sketch. -//! -//! Sketches are loaded through Bevy's asset system, which provides automatic file watching -//! and change detection. This enables hot-reloading workflows where artists can edit their -//! sketch code and see changes reflected immediately without restarting. -//! -//! This module is intentionally language-agnostic — it only handles loading source text from -//! disk. Language-specific crates (like `processing_pyo3`) are responsible for executing the -//! source and binding it to the Processing API. use bevy::{ - asset::{AssetLoader, LoadContext, io::Reader}, + asset::{ + AssetLoader, AssetPath, LoadContext, + io::{AssetSourceId, Reader}, + }, prelude::*, }; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; /// Plugin that registers the Sketch asset type and its loader. pub struct LivecodePlugin; @@ -20,10 +15,21 @@ pub struct LivecodePlugin; impl Plugin for LivecodePlugin { fn build(&self, app: &mut App) { app.init_asset::() - .init_asset_loader::(); + .init_asset_loader::() + .add_systems(Startup, load_current_sketch); } } +fn load_current_sketch(asset_server: Res) { + let path = Path::new("rectangle.py"); + let source = AssetSourceId::from("sketch_directory"); + let _asset_path = AssetPath::from_path(path).with_source(source); + + dbg!("OKOKOKOK {:?}", _asset_path); + + let _h: Handle = asset_server.load(path); +} + /// A sketch source file loaded as a Bevy asset. /// /// The `Sketch` asset contains the raw source code as a string. It does not interpret From cda2db0a2b2bc291bc72e6d12604a8529ff966ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Moon=20Dav=C3=A9?= Date: Thu, 22 Jan 2026 18:40:43 -0500 Subject: [PATCH 4/8] register_asset_source before plugin configuration --- crates/processing_render/src/lib.rs | 33 +++++++++++++++----------- crates/processing_render/src/sketch.rs | 16 +++++++++---- 2 files changed, 30 insertions(+), 19 deletions(-) diff --git a/crates/processing_render/src/lib.rs b/crates/processing_render/src/lib.rs index f9d166c..331b4dd 100644 --- a/crates/processing_render/src/lib.rs +++ b/crates/processing_render/src/lib.rs @@ -213,6 +213,21 @@ fn create_app(config: Config) -> App { app.insert_resource(config.clone()); + if let Some(asset_path) = config.get(ConfigKey::AssetRootPath) { + app.register_asset_source( + "assets_directory", + AssetSourceBuilder::platform_default(asset_path, None), + ); + } + + if let Some(sketch_path) = config.get(ConfigKey::SketchRootPath) { + println!("DEBUG SKETCH PATH = {sketch_path}"); + app.register_asset_source( + "sketch_directory", + AssetSourceBuilder::platform_default(sketch_path, None), + ); + } + #[cfg(not(target_arch = "wasm32"))] let plugins = DefaultPlugins .build() @@ -235,21 +250,11 @@ fn create_app(config: Config) -> App { ..default() }); - if let Some(asset_path) = config.get(ConfigKey::AssetRootPath) { - app.register_asset_source( - "assets_directory", - AssetSourceBuilder::platform_default(asset_path, None), - ); - } - app.add_plugins(plugins); - if let Some(sketch_path) = config.get(ConfigKey::SketchRootPath) { - println!("DEBUG SKETCH PATH = {sketch_path}"); - app.register_asset_source( - "sketch_directory", - AssetSourceBuilder::platform_default(sketch_path, None), - ) - .add_plugins(sketch::LivecodePlugin); + + if let Some(_) = config.get(ConfigKey::SketchRootPath) { + info!("Adding plugin"); + app.add_plugins(sketch::LivecodePlugin); } app.add_plugins(( diff --git a/crates/processing_render/src/sketch.rs b/crates/processing_render/src/sketch.rs index 4da828f..6cb0a8f 100644 --- a/crates/processing_render/src/sketch.rs +++ b/crates/processing_render/src/sketch.rs @@ -17,17 +17,23 @@ impl Plugin for LivecodePlugin { app.init_asset::() .init_asset_loader::() .add_systems(Startup, load_current_sketch); + // .add_systems(Update, test_system); + let render_app = app.sub_app_mut(bevy::render::RenderApp); + render_app.add_systems(ExtractSchedule, test_system); } } +fn test_system() { + info!("DEBUG: calling test_system"); + assert!(false); +} + fn load_current_sketch(asset_server: Res) { + info!("DEBUG: calling load_current_sketch"); let path = Path::new("rectangle.py"); let source = AssetSourceId::from("sketch_directory"); - let _asset_path = AssetPath::from_path(path).with_source(source); - - dbg!("OKOKOKOK {:?}", _asset_path); - - let _h: Handle = asset_server.load(path); + let asset_path = AssetPath::from_path(path).with_source(source); + let _h: Handle = asset_server.load(asset_path); } /// A sketch source file loaded as a Bevy asset. From 709cd4c4339320b2e648bbd4795e40033f11d90c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Moon=20Dav=C3=A9?= Date: Fri, 23 Jan 2026 18:27:23 -0500 Subject: [PATCH 5/8] Created SketchRoot to hold onto Handle --- crates/processing_render/src/sketch.rs | 51 +++++++++++++++++--------- 1 file changed, 33 insertions(+), 18 deletions(-) diff --git a/crates/processing_render/src/sketch.rs b/crates/processing_render/src/sketch.rs index 6cb0a8f..ddaba06 100644 --- a/crates/processing_render/src/sketch.rs +++ b/crates/processing_render/src/sketch.rs @@ -6,6 +6,7 @@ use bevy::{ io::{AssetSourceId, Reader}, }, prelude::*, + render::Extract, }; use std::path::{Path, PathBuf}; @@ -15,40 +16,54 @@ pub struct LivecodePlugin; impl Plugin for LivecodePlugin { fn build(&self, app: &mut App) { app.init_asset::() - .init_asset_loader::() - .add_systems(Startup, load_current_sketch); + .init_asset_loader::(); + + app.add_systems(PreStartup, load_current_sketch); // .add_systems(Update, test_system); - let render_app = app.sub_app_mut(bevy::render::RenderApp); - render_app.add_systems(ExtractSchedule, test_system); } } -fn test_system() { - info!("DEBUG: calling test_system"); - assert!(false); +fn test_system(mut events: Extract>>) { + for event in events.read() { + match event { + AssetEvent::Added { id } => { + info!("Added: {id}") + } + AssetEvent::Modified { id } => { + info!("Modified: {id}") + } + AssetEvent::Removed { id } => { + info!("Removed: {id}") + } + AssetEvent::Unused { id } => { + info!("Unused: {id}") + } + AssetEvent::LoadedWithDependencies { id } => { + info!("LoadedWithDependencies: {id}") + } + } + } } -fn load_current_sketch(asset_server: Res) { +fn load_current_sketch(mut commands: Commands, asset_server: Res) { info!("DEBUG: calling load_current_sketch"); let path = Path::new("rectangle.py"); let source = AssetSourceId::from("sketch_directory"); let asset_path = AssetPath::from_path(path).with_source(source); - let _h: Handle = asset_server.load(asset_path); + let sketch_handle: Handle = asset_server.load(asset_path); + commands.spawn(SketchRoot(sketch_handle)); } +/// `SketchRoot` is what will be spawned and will contain a `Handle` to the `Sketch` +#[derive(Component)] +pub struct SketchRoot(pub Handle); + /// A sketch source file loaded as a Bevy asset. /// /// The `Sketch` asset contains the raw source code as a string. It does not interpret /// or execute the code — that responsibility belongs to language-specific crates. #[derive(Asset, TypePath, Debug)] -pub struct Sketch { - /// The source code contents of the sketch file. - pub source: String, - - /// The original file path. - pub path: PathBuf, -} - +pub struct Sketch; /// Loads sketch files from disk. /// /// Currently supports `.py` files, but the loader is designed to be extended @@ -79,7 +94,7 @@ impl AssetLoader for SketchLoader { let asset_path = load_context.path(); let path: PathBuf = asset_path.path().to_path_buf(); - Ok(Sketch { source, path }) + Ok(Sketch) } fn extensions(&self) -> &[&str] { From dd57a85a974ad5638e6ef830c0210399ae0594de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Moon=20Dav=C3=A9?= Date: Sat, 24 Jan 2026 13:25:29 -0500 Subject: [PATCH 6/8] fix AssetEvent handler for Sketch entities --- crates/processing_render/src/sketch.rs | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/crates/processing_render/src/sketch.rs b/crates/processing_render/src/sketch.rs index ddaba06..3f8337d 100644 --- a/crates/processing_render/src/sketch.rs +++ b/crates/processing_render/src/sketch.rs @@ -6,9 +6,8 @@ use bevy::{ io::{AssetSourceId, Reader}, }, prelude::*, - render::Extract, }; -use std::path::{Path, PathBuf}; +use std::path::Path; /// Plugin that registers the Sketch asset type and its loader. pub struct LivecodePlugin; @@ -18,12 +17,13 @@ impl Plugin for LivecodePlugin { app.init_asset::() .init_asset_loader::(); - app.add_systems(PreStartup, load_current_sketch); - // .add_systems(Update, test_system); + app.add_systems(PreStartup, load_current_sketch) + .add_systems(Update, sketch_update_handler); } } -fn test_system(mut events: Extract>>) { +// TODO: A better name is possible +fn sketch_update_handler(mut events: MessageReader>) { for event in events.read() { match event { AssetEvent::Added { id } => { @@ -63,7 +63,10 @@ pub struct SketchRoot(pub Handle); /// The `Sketch` asset contains the raw source code as a string. It does not interpret /// or execute the code — that responsibility belongs to language-specific crates. #[derive(Asset, TypePath, Debug)] -pub struct Sketch; +pub struct Sketch { + source: String, +} + /// Loads sketch files from disk. /// /// Currently supports `.py` files, but the loader is designed to be extended @@ -80,7 +83,7 @@ impl AssetLoader for SketchLoader { &self, reader: &mut dyn Reader, _settings: &Self::Settings, - load_context: &mut LoadContext<'_>, + _load_context: &mut LoadContext<'_>, ) -> Result { let mut source = String::new(); @@ -91,10 +94,9 @@ impl AssetLoader for SketchLoader { source = utf8.to_string(); } - let asset_path = load_context.path(); - let path: PathBuf = asset_path.path().to_path_buf(); + info!(source); - Ok(Sketch) + Ok(Sketch { source }) } fn extensions(&self) -> &[&str] { From d296565f13f3e9d2fda4292852b21800bfcc1f5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Moon=20Dav=C3=A9?= Date: Sat, 24 Jan 2026 20:21:03 -0500 Subject: [PATCH 7/8] Set a needs update flag on sketch updates --- crates/processing_render/src/sketch.rs | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/crates/processing_render/src/sketch.rs b/crates/processing_render/src/sketch.rs index 3f8337d..cf6637e 100644 --- a/crates/processing_render/src/sketch.rs +++ b/crates/processing_render/src/sketch.rs @@ -9,28 +9,36 @@ use bevy::{ }; use std::path::Path; +#[derive(Resource)] +struct SketchNeedsReload(bool); + /// Plugin that registers the Sketch asset type and its loader. pub struct LivecodePlugin; impl Plugin for LivecodePlugin { fn build(&self, app: &mut App) { app.init_asset::() - .init_asset_loader::(); - - app.add_systems(PreStartup, load_current_sketch) + .init_asset_loader::() + .insert_resource(SketchNeedsReload(false)) + .add_systems(PreStartup, load_current_sketch) .add_systems(Update, sketch_update_handler); } } // TODO: A better name is possible -fn sketch_update_handler(mut events: MessageReader>) { +fn sketch_update_handler( + mut events: MessageReader>, + mut needs_reload: ResMut, +) { for event in events.read() { match event { AssetEvent::Added { id } => { info!("Added: {id}") } AssetEvent::Modified { id } => { - info!("Modified: {id}") + info!("Modified: {id}"); + // we want to emit some event to bevy?? + needs_reload.0 = true; } AssetEvent::Removed { id } => { info!("Removed: {id}") From 1245ff6aa0c9d00e33418fbc7710bcf7c97c6b2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Moon=20Dav=C3=A9?= Date: Mon, 26 Jan 2026 08:14:08 -0500 Subject: [PATCH 8/8] Sketch reloading when running python --- crates/processing_pyo3/src/graphics.rs | 20 ++++++++++++++ crates/processing_pyo3/src/lib.rs | 36 +++++++++++++++++++++++--- crates/processing_render/src/lib.rs | 9 +++++++ crates/processing_render/src/sketch.rs | 24 +++++++++++------ 4 files changed, 78 insertions(+), 11 deletions(-) diff --git a/crates/processing_pyo3/src/graphics.rs b/crates/processing_pyo3/src/graphics.rs index a17e449..4c5812a 100644 --- a/crates/processing_pyo3/src/graphics.rs +++ b/crates/processing_pyo3/src/graphics.rs @@ -61,6 +61,11 @@ impl Topology { } } +#[pyclass] +pub struct Sketch { + pub source: String, +} + #[pymethods] impl Geometry { #[new] @@ -146,6 +151,21 @@ impl Graphics { }) } + pub fn poll_for_sketch_update(&self) -> PyResult { + match poll_for_sketch_updates().map_err(|_| PyRuntimeError::new_err("SKETCH UPDATE ERR"))? { + Some(sketch) => Ok(Sketch { + source: sketch.source, + }), + None => Ok(Sketch { + source: "".to_string(), + }), + } + + // Ok(Sketch { + // source: sketch.unwrap().source, + // }) + } + pub fn background(&self, args: Vec) -> PyResult<()> { let (r, g, b, a) = parse_color(&args)?; let color = bevy::color::Color::srgba(r, g, b, a); diff --git a/crates/processing_pyo3/src/lib.rs b/crates/processing_pyo3/src/lib.rs index 3985d24..fb088fd 100644 --- a/crates/processing_pyo3/src/lib.rs +++ b/crates/processing_pyo3/src/lib.rs @@ -12,7 +12,13 @@ mod glfw; mod graphics; use graphics::{Geometry, Graphics, Image, Topology, get_graphics, get_graphics_mut}; -use pyo3::{exceptions::PyRuntimeError, prelude::*, types::PyTuple}; +use pyo3::{ + exceptions::PyRuntimeError, + ffi::c_str, + prelude::*, + types::{PyDict, PyTuple}, +}; +use std::ffi::{CStr, CString}; use std::env; @@ -81,8 +87,8 @@ fn run(module: &Bound<'_, PyModule>) -> PyResult<()> { let builtins = PyModule::import(py, "builtins")?; let locals = builtins.getattr("locals")?.call0()?; - let setup_fn = locals.get_item("setup")?; - let draw_fn = locals.get_item("draw")?; + let mut setup_fn = locals.get_item("setup")?; + let mut draw_fn = locals.get_item("draw")?; // call setup setup_fn.call0()?; @@ -91,6 +97,30 @@ fn run(module: &Bound<'_, PyModule>) -> PyResult<()> { loop { { let mut graphics = get_graphics_mut(module)?; + + // TODO: this shouldn't be on the graphics object + let sketch = graphics.poll_for_sketch_update()?; + if !sketch.source.is_empty() { + let locals = PyDict::new(py); + + let ok = CString::new(sketch.source.as_str()).unwrap(); + let cstr: &CStr = ok.as_c_str(); + + match py.run(cstr, None, Some(&locals)) { + Ok(_) => { + dbg!("Success of any kind?"); + } + Err(e) => { + dbg!(e); + } + } + + setup_fn = locals.get_item("setup").unwrap().unwrap(); + draw_fn = locals.get_item("draw").unwrap().unwrap(); + + dbg!(locals); + } + if !graphics.surface.poll_events() { break; } diff --git a/crates/processing_render/src/lib.rs b/crates/processing_render/src/lib.rs index 331b4dd..5006890 100644 --- a/crates/processing_render/src/lib.rs +++ b/crates/processing_render/src/lib.rs @@ -1086,3 +1086,12 @@ pub fn geometry_box(width: f32, height: f32, depth: f32) -> error::Result error::Result> { + app_mut(|app| { + Ok(app + .world_mut() + .run_system_cached(sketch::sketch_update_handler) + .unwrap()) + }) +} diff --git a/crates/processing_render/src/sketch.rs b/crates/processing_render/src/sketch.rs index cf6637e..79e35de 100644 --- a/crates/processing_render/src/sketch.rs +++ b/crates/processing_render/src/sketch.rs @@ -19,17 +19,18 @@ impl Plugin for LivecodePlugin { fn build(&self, app: &mut App) { app.init_asset::() .init_asset_loader::() + // TODO: this could be switched to Message .insert_resource(SketchNeedsReload(false)) - .add_systems(PreStartup, load_current_sketch) - .add_systems(Update, sketch_update_handler); + .add_systems(PreStartup, load_current_sketch); + // .add_systems(Update, sketch_update_handler); } } // TODO: A better name is possible -fn sketch_update_handler( +pub fn sketch_update_handler( mut events: MessageReader>, - mut needs_reload: ResMut, -) { + sketches: Res>, +) -> Option { for event in events.read() { match event { AssetEvent::Added { id } => { @@ -38,7 +39,11 @@ fn sketch_update_handler( AssetEvent::Modified { id } => { info!("Modified: {id}"); // we want to emit some event to bevy?? - needs_reload.0 = true; + // needs_reload.0 = true; + if let Some(sketch) = sketches.get(*id) { + let sketch = sketch.clone(); + return Some(sketch); + } } AssetEvent::Removed { id } => { info!("Removed: {id}") @@ -51,6 +56,8 @@ fn sketch_update_handler( } } } + + None } fn load_current_sketch(mut commands: Commands, asset_server: Res) { @@ -70,9 +77,10 @@ pub struct SketchRoot(pub Handle); /// /// The `Sketch` asset contains the raw source code as a string. It does not interpret /// or execute the code — that responsibility belongs to language-specific crates. -#[derive(Asset, TypePath, Debug)] +#[derive(Asset, Clone, TypePath, Debug)] pub struct Sketch { - source: String, + // TODO: should this be &str ? + pub source: String, } /// Loads sketch files from disk.