host_fns.rs (5699B)
1 use crate::commands::{button_key, UiCommand}; 2 use std::collections::HashMap; 3 use wasmer::{FunctionEnv, FunctionEnvMut, Imports, Memory, MemoryView, Store}; 4 5 pub struct HostEnv { 6 pub memory: Option<Memory>, 7 pub commands: Vec<UiCommand>, 8 pub button_events: HashMap<String, bool>, 9 pub button_occ: HashMap<String, u32>, 10 pub available_width: f32, 11 pub available_height: f32, 12 } 13 14 impl HostEnv { 15 pub fn new() -> Self { 16 Self { 17 memory: None, 18 commands: Vec::new(), 19 button_events: HashMap::new(), 20 button_occ: HashMap::new(), 21 available_width: 0.0, 22 available_height: 0.0, 23 } 24 } 25 } 26 27 /// Read a UTF-8 string from WASM linear memory. 28 fn read_wasm_str(view: &MemoryView, ptr: i32, len: i32) -> Option<String> { 29 let ptr = ptr as u64; 30 let len = len as usize; 31 let mut buf = vec![0u8; len]; 32 view.read(ptr, &mut buf).ok()?; 33 String::from_utf8(buf).ok() 34 } 35 36 /// Register all host imports into the given store. 37 pub fn create_imports(store: &mut Store, env: &FunctionEnv<HostEnv>) -> Imports { 38 use wasmer::Function; 39 40 // --- Widgets --- 41 42 fn nd_label(mut env: FunctionEnvMut<HostEnv>, ptr: i32, len: i32) { 43 let (data, store) = env.data_and_store_mut(); 44 let memory = data.memory.as_ref().expect("memory not set"); 45 let view = memory.view(&store); 46 if let Some(text) = read_wasm_str(&view, ptr, len) { 47 data.commands.push(UiCommand::Label(text)); 48 } 49 } 50 51 fn nd_heading(mut env: FunctionEnvMut<HostEnv>, ptr: i32, len: i32) { 52 let (data, store) = env.data_and_store_mut(); 53 let memory = data.memory.as_ref().expect("memory not set"); 54 let view = memory.view(&store); 55 if let Some(text) = read_wasm_str(&view, ptr, len) { 56 data.commands.push(UiCommand::Heading(text)); 57 } 58 } 59 60 fn nd_button(mut env: FunctionEnvMut<HostEnv>, ptr: i32, len: i32) -> i32 { 61 let (data, store) = env.data_and_store_mut(); 62 let memory = data.memory.as_ref().expect("memory not set"); 63 let view = memory.view(&store); 64 if let Some(text) = read_wasm_str(&view, ptr, len) { 65 let occ = data.button_occ.entry(text.clone()).or_insert(0); 66 let key = button_key(&text, *occ); 67 *occ += 1; 68 let clicked = data.button_events.get(&key).copied().unwrap_or(false); 69 data.commands.push(UiCommand::Button(text)); 70 if clicked { 71 1 72 } else { 73 0 74 } 75 } else { 76 0 77 } 78 } 79 80 fn nd_add_space(mut env: FunctionEnvMut<HostEnv>, pixels: f32) { 81 let (data, _store) = env.data_and_store_mut(); 82 data.commands.push(UiCommand::AddSpace(pixels)); 83 } 84 85 // --- Layout queries --- 86 87 fn nd_available_width(env: FunctionEnvMut<HostEnv>) -> f32 { 88 env.data().available_width 89 } 90 91 fn nd_available_height(env: FunctionEnvMut<HostEnv>) -> f32 { 92 env.data().available_height 93 } 94 95 // --- Drawing primitives (coordinates relative to app rect) --- 96 97 fn nd_draw_rect(mut env: FunctionEnvMut<HostEnv>, x: f32, y: f32, w: f32, h: f32, color: i32) { 98 let (data, _store) = env.data_and_store_mut(); 99 data.commands.push(UiCommand::DrawRect { 100 x, 101 y, 102 w, 103 h, 104 color: color as u32, 105 }); 106 } 107 108 fn nd_draw_circle(mut env: FunctionEnvMut<HostEnv>, cx: f32, cy: f32, r: f32, color: i32) { 109 let (data, _store) = env.data_and_store_mut(); 110 data.commands.push(UiCommand::DrawCircle { 111 cx, 112 cy, 113 r, 114 color: color as u32, 115 }); 116 } 117 118 fn nd_draw_line( 119 mut env: FunctionEnvMut<HostEnv>, 120 x1: f32, 121 y1: f32, 122 x2: f32, 123 y2: f32, 124 width: f32, 125 color: i32, 126 ) { 127 let (data, _store) = env.data_and_store_mut(); 128 data.commands.push(UiCommand::DrawLine { 129 x1, 130 y1, 131 x2, 132 y2, 133 width, 134 color: color as u32, 135 }); 136 } 137 138 fn nd_draw_text( 139 mut env: FunctionEnvMut<HostEnv>, 140 x: f32, 141 y: f32, 142 ptr: i32, 143 len: i32, 144 size: f32, 145 color: i32, 146 ) { 147 let (data, store) = env.data_and_store_mut(); 148 let memory = data.memory.as_ref().expect("memory not set"); 149 let view = memory.view(&store); 150 if let Some(text) = read_wasm_str(&view, ptr, len) { 151 data.commands.push(UiCommand::DrawText { 152 x, 153 y, 154 text, 155 size, 156 color: color as u32, 157 }); 158 } 159 } 160 161 wasmer::imports! { 162 "env" => { 163 "nd_label" => Function::new_typed_with_env(store, env, nd_label), 164 "nd_heading" => Function::new_typed_with_env(store, env, nd_heading), 165 "nd_button" => Function::new_typed_with_env(store, env, nd_button), 166 "nd_add_space" => Function::new_typed_with_env(store, env, nd_add_space), 167 "nd_available_width" => Function::new_typed_with_env(store, env, nd_available_width), 168 "nd_available_height" => Function::new_typed_with_env(store, env, nd_available_height), 169 "nd_draw_rect" => Function::new_typed_with_env(store, env, nd_draw_rect), 170 "nd_draw_circle" => Function::new_typed_with_env(store, env, nd_draw_circle), 171 "nd_draw_line" => Function::new_typed_with_env(store, env, nd_draw_line), 172 "nd_draw_text" => Function::new_typed_with_env(store, env, nd_draw_text), 173 } 174 } 175 }