From dfcc06fb3130259e0bad50457f7f666cfd672c5d Mon Sep 17 00:00:00 2001 From: chayleaf Date: Wed, 4 Sep 2024 07:35:50 +0700 Subject: [PATCH] scheme support refactoring --- src/image.rs | 2 +- src/keyboard.rs | 205 ++++++++++++----- src/keyboard/script.rs | 469 ++++++++++++++++++++++++++++++++++++++ src/main.rs | 506 +---------------------------------------- src/script.rs | 93 +++++--- src/text.rs | 13 +- 6 files changed, 694 insertions(+), 594 deletions(-) create mode 100644 src/keyboard/script.rs diff --git a/src/image.rs b/src/image.rs index a2d063d..8b68662 100644 --- a/src/image.rs +++ b/src/image.rs @@ -2,7 +2,7 @@ use std::{marker::PhantomData, mem, slice::ChunksExactMut}; use serde::{Deserialize, Serialize}; -#[derive(Copy, Clone, Eq, PartialEq)] +#[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum PixelFormat { Argb8888Le, } diff --git a/src/keyboard.rs b/src/keyboard.rs index 9596b63..16c4e59 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -1,12 +1,15 @@ -use std::{collections::HashMap, time::Instant}; +use std::{collections::HashMap, fmt::Debug}; use crate::{ graphics::{self, LazyCanvas}, image::PixelFormat, - script::{Engine, Value}, + script::{Engine, Func, ValTrait, Value}, text::FontDb, }; +mod script; + +#[derive(Clone, Debug)] pub struct Popup { left: i32, top: i32, @@ -21,6 +24,7 @@ pub struct PopupInfo<'b> { font_db: &'b mut FontDb, } +#[derive(Clone, Debug)] pub struct Class { pub supers: Vec, pub methods: HashMap, @@ -43,18 +47,24 @@ pub struct Keyboard { fmt: PixelFormat, pub(crate) font_db: FontDb, is_first_draw: bool, - config_handlers: HashMap< - String, - Vec<( - usize, - Box, - )>, - >, - timers: Vec<(Instant, Box)>, - config_handler_id: usize, - pub(crate) user_data: HashMap>, - pub(crate) classes: HashMap>, - pub(crate) env: Option>, + pub(crate) user_data: HashMap>, + pub(crate) classes: HashMap>, + /// SAFETY: unsafe + engine: E, +} +impl Debug for Keyboard { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Keyboard") + .field("layout", &self.layout) + .field("popup", &self.popup) + .field("width", &self.width) + .field("height", &self.height) + .field("fmt", &self.fmt) + .field("font_db", &self.font_db) + .field("is_first_draw", &self.is_first_draw) + .field("classes", &self.classes) + .finish_non_exhaustive() + } } impl<'b> PopupInfo<'b> { @@ -127,15 +137,9 @@ pub struct DamageInfo { pub height: u32, } -impl Default for Keyboard { - fn default() -> Self { - Self::new() - } -} - -impl Keyboard { - pub fn new() -> Self { - Self { +impl Keyboard { + pub fn new(engine: E) -> Self { + let mut ret = Self { width: 720, height: 480, font_db: Default::default(), @@ -143,16 +147,16 @@ impl Keyboard { layout: None, popup: None, is_first_draw: true, - config_handlers: HashMap::new(), - config_handler_id: 0, - timers: Vec::new(), user_data: HashMap::new(), classes: HashMap::new(), - env: None, - } + engine, + }; + script::init_builtins(&mut ret); // ret.layout = Some(ret.create_layout()); - // ret + ret } +} +impl Keyboard { pub fn size(&self) -> (u32, u32) { (self.width, self.height) } @@ -189,24 +193,6 @@ impl Keyboard { is_first_draw: true, });*/ } - pub fn _add_settings_handler( - &mut self, - key: &str, - handler: impl 'static + FnMut(&Self, &str, &toml_edit::Value), - ) -> usize { - let id = self.config_handler_id; - self.config_handler_id += 1; - self.config_handlers - .entry(key.to_owned()) - .or_default() - .push((id, Box::new(handler))); - id - } - pub fn _remove_settings_handler(&mut self, key: &str, id: usize) { - if let Some(x) = self.config_handlers.get_mut(key) { - x.retain(|(i, _)| id != *i); - } - } pub fn hide(&mut self) { self.popup = None; } @@ -235,10 +221,7 @@ impl Keyboard { .and_then(|cls| cls.methods.get_mut("kbd/width-changed")) .cloned() { - let mut env = self.env.take().unwrap(); - env.call(self, &func, vec![Value::Symbol("en".into())]) - .unwrap(); - self.env = Some(env); + func.call(self, vec![Value::Symbol("en".into())]).unwrap(); } self.is_first_draw = false; if let Some(layout) = &mut self.layout { @@ -361,3 +344,123 @@ impl Keyboard { layout }*/ } + +#[repr(transparent)] +pub struct ValueWrapper(E::Value); +impl ValTrait for ValueWrapper { + type Engine = Keyboard; + fn from_val2(val: Value, engine: &mut Self::Engine) -> Self { + Self(E::Value::from_val2(val.convert(), &mut engine.engine)) + } + fn from_val(val: Value) -> Self { + Self(E::Value::from_val(val.convert())) + } + fn to_val(&self) -> Value { + self.0.to_val().convert() + } + fn to_val2(&self, engine: &Self::Engine) -> Value { + self.0.to_val2(&engine.engine).convert() + } +} +#[repr(transparent)] +#[derive(Debug)] +pub struct FuncWrapper(E::Func); +impl Clone for FuncWrapper +where + E::Func: Clone, +{ + fn clone(&self) -> Self { + Self(self.0.clone()) + } +} +impl Ord for FuncWrapper +where + E::Func: Ord, +{ + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.0.cmp(&other.0) + } +} +impl Eq for FuncWrapper where E::Func: Eq {} +impl PartialOrd for FuncWrapper +where + E::Func: PartialOrd, +{ + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.0.cmp(&other.0)) + } +} +impl PartialEq for FuncWrapper +where + E::Func: PartialEq, +{ + fn eq(&self, other: &Self) -> bool { + self.0.eq(&other.0) + } +} +impl Func for FuncWrapper { + type BasicValue = ::BasicValue; + fn from_basic_value(v: Self::BasicValue) -> Self { + Self(E::Func::from_basic_value(v)) + } + fn into_basic_value(self) -> Self::BasicValue { + self.0.into_basic_value() + } + type Engine = Keyboard; + fn call( + &self, + engine: &mut Self::Engine, + args: Vec>, + ) -> Result, ::Error> { + Ok(self + .0 + .call( + &mut engine.engine, + args.into_iter().map(Value::convert).collect(), + )? + .convert()) + } + fn unref(&mut self, engine: &mut Self::Engine) { + self.0.unref(&mut engine.engine); + } + fn add_ref(&mut self, engine: &mut Self::Engine) { + self.0.add_ref(&mut engine.engine); + } +} + +impl Engine for Keyboard { + type Value = ValueWrapper; + type Error = E::Error; + type Func = FuncWrapper; + fn eval_t(&mut self, text: &str) -> Result, Self::Error> { + Ok(self.engine.eval_t(text)?.convert()) + } + fn gc(&mut self) { + self.engine.gc(); + } + fn register(&mut self, name: &str, val: Value) { + self.engine.register(name, val.convert()) + } + #[inline(always)] + fn register_func( + &mut self, + name: &'static str, + func: impl 'static + Fn(&mut Self, Vec>) -> Result, Self::Error>, + ) { + self.engine.register_func(name, move |a: &mut E, b| { + Ok(func( + // SAFETY: as long as engine isn't used directly by the Keyboard, + // and self is used as the engine itself, this is fine + unsafe { + let ofs = std::mem::offset_of!(Self, engine); + let ptr: *mut Self = ((a as *mut E) as *mut Self).byte_sub(ofs); + &mut *ptr + }, + // SAFETY: should have the same layout, + // because FuncWrapper is repr(transparent) + unsafe { std::mem::transmute::>, Vec>>(b) }, + )? + .convert()) + }) + } +} diff --git a/src/keyboard/script.rs b/src/keyboard/script.rs new file mode 100644 index 0000000..9b2d337 --- /dev/null +++ b/src/keyboard/script.rs @@ -0,0 +1,469 @@ +use crate::{ + graphics, image, + script::{Args, Engine, ErrorTrait, Func, Value}, +}; + +use super::Keyboard; + +fn register( + vm: &mut Keyboard, + args: Args>, +) -> Result>, as Engine>::Error> { + let Ok::<[Value<_>; 3], _>([cls, name, func]) = args.try_into() else { + return Err(ErrorTrait::invalid_argc("kbd/register")); + }; + let Value::Symbol(cls) = cls else { + return Err(ErrorTrait::invalid_argt( + "kbd/register", + "symbol", + &format!("{cls:?}"), + )); + }; + let Value::Symbol(name) = name else { + return Err(ErrorTrait::invalid_argt( + "kbd/register", + "symbol", + &format!("{name:?}"), + )); + }; + let Value::Func(mut func) = func else { + return Err(ErrorTrait::invalid_argt( + "kbd/register", + "function", + &format!("{func:?}"), + )); + }; + func.add_ref(vm); + println!("register {cls:?} {name:?} {func:?}"); + vm.classes + .entry(cls.into_owned()) + .or_default() + .methods + .insert(name.into_owned(), func); + println!("done"); + + Ok(Value::Null) +} + +fn class_define( + vm: &mut Keyboard, + args: Args>, +) -> Result>, as Engine>::Error> { + let Ok::<[Value<_>; 2], _>([cls, supers]) = args.try_into() else { + return Err(ErrorTrait::invalid_argc("kbd/class-define")); + }; + let Value::Symbol(cls) = cls else { + return Err(ErrorTrait::invalid_argt( + "kbd/class-define", + "symbol", + &format!("{cls:?}"), + )); + }; + let supers = match supers { + Value::Null => vec![], + Value::RevArray(supers) => supers, + _ => { + return Err(ErrorTrait::invalid_argt( + "kbd/class-define", + "list", + &format!("{supers:?}"), + )); + } + }; + let supers = supers + .into_iter() + .rev() + .map(|sup| { + let Value::Symbol(sup) = sup else { + return Err(ErrorTrait::invalid_argt( + "kbd/class-define", + "list of symbols", + &format!("list containing {sup:?}"), + )); + }; + Ok(sup.into_owned()) + }) + .collect::, _>>()?; + println!("register {cls:?} {supers:?}"); + + vm.classes + .entry(cls.into_owned()) + .or_default() + .supers + .extend(supers); + println!("done"); + Ok(Value::Null) +} + +fn call( + vm: &mut Keyboard, + mut args: Args>, +) -> Result>, as Engine>::Error> { + args.reverse(); + let cls = args + .pop() + .ok_or_else(|| ErrorTrait::invalid_argc("kbd/call"))?; + let name = args + .pop() + .ok_or_else(|| ErrorTrait::invalid_argc("kbd/call"))?; + let Value::Symbol(cls) = cls else { + return Err(ErrorTrait::invalid_argt( + "kbd/call", + "symbol", + &format!("{cls:?}"), + )); + }; + let Value::Symbol(name) = name else { + return Err(ErrorTrait::invalid_argt( + "kbd/call", + "symbol", + &format!("{name:?}"), + )); + }; + fn call_func( + func: &mut E::Func, + engine: &mut E, + args: Vec>, + ) -> Result, E::Error> { + func.call(engine, args) + } + let func = vm + .classes + .get_mut(cls.as_ref()) + .and_then(|cls| cls.methods.get_mut(name.as_ref()).cloned()); + if let Some(mut func) = func { + args.push(Value::Symbol(cls)); + args.reverse(); + call_func(&mut func, vm, args) + } else { + Err(ErrorTrait::from_str( + "kbd/call", + &format!("{cls}/{name} is not defined"), + )) + } +} + +fn set_user_data( + vm: &mut Keyboard, + args: Args>, +) -> Result>, as Engine>::Error> { + let Ok::<[Value<_>; 2], _>([k, mut v]) = args.try_into() else { + return Err(ErrorTrait::invalid_argc("kbd/set-user-data")); + }; + let Value::Symbol(k) = k else { + return Err(ErrorTrait::invalid_argt( + "kbd/set-user-data", + "symbol", + &format!("{k:?}"), + )); + }; + println!("setting user data {k:?} to {v:?}"); + if let Some(mut val) = vm.user_data.insert(k.into_owned(), v.clone()) { + // FIXME: theoretically this can unref data that's referenced elsewhere + val.unref(vm); + } + v.add_ref(vm); + Ok(Value::Null) +} + +fn get_user_data( + vm: &mut Keyboard, + args: Args>, +) -> Result>, as Engine>::Error> { + let Ok::<[Value<_>; 1], _>([k]) = args.try_into() else { + return Err(ErrorTrait::invalid_argc("kbd/get-user-data")); + }; + let Value::Symbol(k) = k else { + return Err(ErrorTrait::invalid_argt( + "kbd/get-user-data", + "symbol", + &format!("{k:?}"), + )); + }; + println!("getting user data {k:?}"); + Ok(vm + .user_data + .get(k.as_ref()) + .cloned() + .unwrap_or(Value::Bool(false))) +} + +fn add_to_i64(name: &str, a: Value, b: Value) -> Result { + Ok(match (a, b) { + (Value::Int(a), Value::Int(b)) => a.saturating_add(b), + (Value::Float(a), Value::Float(b)) => (a + b) as i64, + (Value::Int(a), Value::Float(b)) => (a as f64 + b) as i64, + (Value::Float(a), Value::Int(b)) => (a + b as f64) as i64, + x => { + return Err(E::Error::invalid_argt( + name, + "int or float", + &format!("{:?}", x.1), + )) + } + }) +} +fn to_i64(name: &str, x: Value) -> Result { + add_to_i64(name, x, Value::Int(0)) +} + +fn make_text( + vm: &mut Keyboard, + args: Args>, +) -> Result>, as Engine>::Error> { + let Ok::<[Value<_>; 5], _>([xy, wh, text, font, font_size]) = args.try_into() else { + return Err(ErrorTrait::invalid_argc("kbd/make-text")); + }; + let Value::Pair(xy) = xy else { + return Err(ErrorTrait::invalid_argt( + "kbd/make-text", + "pair", + &format!("{xy:?}"), + )); + }; + let Value::Pair(wh) = wh else { + return Err(ErrorTrait::invalid_argt( + "kbd/make-text", + "pair", + &format!("{wh:?}"), + )); + }; + let Value::String(text) = text else { + return Err(ErrorTrait::invalid_argt( + "kbd/make-text", + "string", + &format!("{text:?}"), + )); + }; + let Value::String(font) = font else { + return Err(ErrorTrait::invalid_argt( + "kbd/make-text", + "string", + &format!("{font:?}"), + )); + }; + let font_size = match font_size { + Value::Int(x) => x as f64, + Value::Float(x) => x, + _ => { + return Err(ErrorTrait::invalid_argt( + "kbd/make-text", + "string", + &format!("{font_size:?}"), + )) + } + }; + let Some(font_handle) = vm.font_db.font_handle(&font) else { + return Err(ErrorTrait::from_str("kbd/make-text", "font not found")); + }; + let (x, y) = *xy; + let (w, h) = *wh; + let x1 = to_i64("kbd/make-text", x.clone())?; + let y1 = to_i64("kbd/make-text", y.clone())?; + let x_w = add_to_i64("kbd/make-text", x, w)?; + let y_h = add_to_i64("kbd/make-text", y, h)?; + let (x, y) = (x1, y1); + let (w, h) = (x_w - x, y_h - y); + let halign = graphics::Alignment::Middle; + let valign = graphics::Alignment::Middle; + let direction = swash::shape::Direction::LeftToRight; + let script = swash::text::Script::Latin; + let color = image::ColorRgba([255, 0, 0, 255]); + // [xy, wh, text, font, font_size] + // println!("rendering text {text} of size {w}/{h} at {x}/{y} with font {font} {font_size}, color {color:?}, alignment {halign:?}/{valign:?}, script {script:?}, direction {direction:?}"); + let text = graphics::WidgetEnum::Text(graphics::Text { + direction, + font_size: font_size as f32, + font_handle, + color, + halign, + valign, + script, + text: text.into(), + pos: graphics::Rect { + left: x as i32, + bottom: y as i32, + height: h as u32, + width: w as u32, + }, + }); + Value::from_serde(&text).map_err(|err| ErrorTrait::from_str("kbd/make-text", &err.to_string())) +} + +fn make_rect( + _vm: &mut Keyboard, + args: Args>, +) -> Result>, as Engine>::Error> { + let mut args = args.into_iter(); + let Some(xy) = args.next() else { + return Err(ErrorTrait::invalid_argc("kbd/make-rect")); + }; + let Some(wh) = args.next() else { + return Err(ErrorTrait::invalid_argc("kbd/make-rect")); + }; + let mut round = 0.0f64; + while let Some(arg) = args.next() { + match arg { + Value::Symbol(x) if x == ":round-corners" => { + round = 1.0; + if let Some(arg) = args.next() { + let Value::Float(arg) = arg else { + return Err(ErrorTrait::invalid_argt( + "kbd/make-rect", + "float", + &format!("{arg:?}"), + )); + }; + round = arg; + } + } + x => { + return Err(ErrorTrait::invalid_argt( + "kbd/make-rect", + "make-rect keyword arguments", + &format!("{x:?}"), + )) + } + } + } + let Value::Pair(xy) = xy else { + return Err(ErrorTrait::invalid_argt( + "kbd/make-rect", + "pair", + &format!("{xy:?}"), + )); + }; + let Value::Pair(wh) = wh else { + return Err(ErrorTrait::invalid_argt( + "kbd/make-rect", + "pair", + &format!("{wh:?}"), + )); + }; + let (x, y) = *xy; + let (w, h) = *wh; + let x1 = to_i64("kbd/make-rect", x.clone())?; + let y1 = to_i64("kbd/make-rect", y.clone())?; + let x_w = add_to_i64("kbd/make-rect", x, w)?; + let y_h = add_to_i64("kbd/make-rect", y, h)?; + let (x, y) = (x1, y1); + let (w, h) = (x_w - x, y_h - y); + let color = image::ColorRgba([0, 0, 0, 255]); + // [xy, wh, text, font, font_size] + println!("rendering rect of size {w}/{h} at {x}/{y} with rounding {round}, color {color:?}"); + let rect = graphics::WidgetEnum::Shape(graphics::SimpleShape { + col: color, + pos: graphics::Rect { + left: x as i32, + bottom: y as i32, + width: w as u32, + height: h as u32, + }, + round_corners_ratio: round as f32, + }); + Value::from_serde(&rect).map_err(|err| ErrorTrait::from_str("kbd/make-rect", &err.to_string())) +} + +fn make_layout( + _vm: &mut Keyboard, + args: Args>, +) -> Result>, as Engine>::Error> { + let Ok::<[Value<_>; 1], _>([items]) = args.try_into() else { + return Err(ErrorTrait::invalid_argc("kbd/make-layout")); + }; + let Value::RevArray(items) = items else { + return Err(ErrorTrait::invalid_argt( + "kbd/make-layout", + "list", + &format!("{items:?}"), + )); + }; + let mut layout = graphics::Layout::new(); + for item in items.into_iter().rev() { + let Value::RevArray(mut item) = item else { + return Err(ErrorTrait::invalid_argt( + "kbd/make-layout", + "list of lists", + &format!("list with {item:?}"), + )); + }; + let Some(Value::String(a)) = item.pop() else { + return Err(ErrorTrait::from_str( + "kbd/make-layout", + "each item's first element must be the item id", + )); + }; + let item = Value::RevArray(item) + .to_serde() + .map_err(|err| ErrorTrait::from_str("kbd/make-layout", &err.to_string()))?; + // println!("{item:#?}"); + layout.add_child(item, &a, None); + } + Value::from_serde(&layout) + .map_err(|err| ErrorTrait::from_str("kbd/make-layout", &err.to_string())) +} + +fn width( + vm: &mut Keyboard, + args: Args>, +) -> Result>, as Engine>::Error> { + let Ok::<[Value<_>; 0], _>([]) = args.try_into() else { + return Err(ErrorTrait::invalid_argc("kbd/width")); + }; + println!("getting keyboard width"); + Ok(Value::Int(vm.width.into())) +} + +fn load_font( + vm: &mut Keyboard, + args: Args>, +) -> Result>, as Engine>::Error> { + let Ok::<[Value<_>; 1], _>([path]) = args.try_into() else { + return Err(ErrorTrait::invalid_argc("kbd/load-font")); + }; + let Value::String(path) = path else { + return Err(ErrorTrait::invalid_argt( + "kbd/load-font", + "string", + &format!("{path:?}"), + )); + }; + println!("loading font {path:?}"); + vm.font_db + .load_font(path) + .map(|()| Value::Null) + .map_err(|err| ErrorTrait::from_str("kbd/load-font", &err.to_string())) +} + +fn set_layout( + vm: &mut Keyboard, + args: Args>, +) -> Result>, as Engine>::Error> { + let Ok::<[Value<_>; 1], _>([layout]) = args.try_into() else { + return Err(ErrorTrait::invalid_argc("kbd/set-layout")); + }; + let layout: graphics::Layout = layout + .to_serde() + .map_err(|err| ErrorTrait::from_str("kbd/set-layout", &err.to_string()))?; + vm.layout = Some(layout); + Ok(Value::Null) +} + +pub fn init_builtins(engine: &mut Keyboard) { + const DEFS: &str = include_str!("../../defs.scm"); + engine.eval_t(DEFS).unwrap(); + engine.gc(); + engine.register_func("kbd/register", register); + engine.register_func("kbd/class-define", class_define); + engine.register_func("kbd/call", call); + engine.register_func("kbd/set-user-data", set_user_data); + engine.register_func("kbd/get-user-data", get_user_data); + engine.register_func("kbd/make-text", make_text); + engine.register(":round-corners", Value::new_symbol(":round-corners")); + engine.register_func("kbd/make-rect", make_rect); + engine.register_func("kbd/make-layout", make_layout); + engine.register_func("kbd/width", width); + engine.register_func("kbd/load-font", load_font); + engine.register_func("kbd/set-layout", set_layout); + engine.gc(); +} diff --git a/src/main.rs b/src/main.rs index 9a21b90..792f037 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,6 @@ #![allow(clippy::type_complexity)] -use std::{cell::RefCell, rc::Rc}; -use script::{Engine, ErrorTrait, Func, Value}; +use script::Engine; #[cfg(feature = "wayland")] pub use wayland_protocols_misc; @@ -17,509 +16,12 @@ mod text; #[cfg(feature = "wayland")] mod wayland; -#[derive(Default)] -struct ScriptEnv(E, Rc>>); - -impl ScriptEnv { - fn eval(&mut self, kbd: &mut Keyboard, text: &str) -> Result, E::Error> { - std::mem::swap(kbd, &mut (*self.1).borrow_mut()); - let ret = self.0.eval_t(text); - std::mem::swap(kbd, &mut (*self.1).borrow_mut()); - ret - } - fn call( - &mut self, - kbd: &mut Keyboard, - func: &E::Func, - args: Vec>, - ) -> Result, E::Error> { - std::mem::swap(kbd, &mut (*self.1).borrow_mut()); - // work with kbd1 - let ret = func.call(&mut self.0, args); - std::mem::swap(kbd, &mut (*self.1).borrow_mut()); - ret - } -} - -fn new_script_env() -> ScriptEnv { - let mut engine = script::create_engine(); - const DEFS: &str = include_str!("../defs.scm"); - let ctx: Rc>> = Rc::default(); - engine.eval_t(DEFS).unwrap(); - engine.gc(); - let ctx1 = ctx.clone(); - engine.register_func( - "kbd/register", - Box::new(move |vm, args| { - let Ok::<[Value<_>; 3], _>([cls, name, func]) = args.try_into() else { - return Err(ErrorTrait::invalid_argc("kbd/register")); - }; - let Value::Symbol(cls) = cls else { - return Err(ErrorTrait::invalid_argt( - "kbd/register", - "symbol", - &format!("{cls:?}"), - )); - }; - let Value::Symbol(name) = name else { - return Err(ErrorTrait::invalid_argt( - "kbd/register", - "symbol", - &format!("{name:?}"), - )); - }; - let Value::Func(mut func) = func else { - return Err(ErrorTrait::invalid_argt( - "kbd/register", - "function", - &format!("{func:?}"), - )); - }; - func.add_ref(vm); - println!("register {cls:?} {name:?} {func:?}"); - (*ctx1) - .borrow_mut() - .classes - .entry(cls.into_owned()) - .or_default() - .methods - .insert(name.into_owned(), func); - println!("done"); - - Ok(Value::Null) - }), - ); - let ctx1 = ctx.clone(); - engine.register_func( - "kbd/class-define", - Box::new(move |_vm, args| { - let Ok::<[Value<_>; 2], _>([cls, supers]) = args.try_into() else { - return Err(ErrorTrait::invalid_argc("kbd/class-define")); - }; - let Value::Symbol(cls) = cls else { - return Err(ErrorTrait::invalid_argt( - "kbd/class-define", - "symbol", - &format!("{cls:?}"), - )); - }; - let supers = match supers { - Value::Null => vec![], - Value::RevArray(supers) => supers, - _ => { - return Err(ErrorTrait::invalid_argt( - "kbd/class-define", - "list", - &format!("{supers:?}"), - )); - } - }; - let supers = supers - .into_iter() - .rev() - .map(|sup| { - let Value::Symbol(sup) = sup else { - return Err(ErrorTrait::invalid_argt( - "kbd/class-define", - "list of symbols", - &format!("list containing {sup:?}"), - )); - }; - Ok(sup.into_owned()) - }) - .collect::, _>>()?; - println!("register {cls:?} {supers:?}"); - - (*ctx1) - .borrow_mut() - .classes - .entry(cls.into_owned()) - .or_default() - .supers - .extend(supers); - println!("done"); - Ok(Value::Null) - }), - ); - let ctx1 = ctx.clone(); - engine.register_func( - "kbd/call", - Box::new(move |vm, mut args| { - args.reverse(); - let cls = args - .pop() - .ok_or_else(|| ErrorTrait::invalid_argc("kbd/call"))?; - let name = args - .pop() - .ok_or_else(|| ErrorTrait::invalid_argc("kbd/call"))?; - let Value::Symbol(cls) = cls else { - return Err(ErrorTrait::invalid_argt( - "kbd/call", - "symbol", - &format!("{cls:?}"), - )); - }; - let Value::Symbol(name) = name else { - return Err(ErrorTrait::invalid_argt( - "kbd/call", - "symbol", - &format!("{name:?}"), - )); - }; - fn call_func( - func: &mut E::Func, - engine: &mut E, - args: Vec>, - ) -> Result, E::Error> { - func.call(engine, args) - } - let func = (*ctx1) - .borrow_mut() - .classes - .get_mut(cls.as_ref()) - .and_then(|cls| cls.methods.get_mut(name.as_ref()).cloned()); - if let Some(mut func) = func { - args.push(Value::Symbol(cls)); - args.reverse(); - call_func(&mut func, vm, args) - } else { - Err(ErrorTrait::from_str( - "kbd/call", - &format!("{cls}/{name} is not defined"), - )) - } - }), - ); - let ctx1 = ctx.clone(); - engine.register_func( - "kbd/set-user-data", - Box::new(move |vm, args| { - let Ok::<[Value<_>; 2], _>([k, mut v]) = args.try_into() else { - return Err(ErrorTrait::invalid_argc("kbd/set-user-data")); - }; - let Value::Symbol(k) = k else { - return Err(ErrorTrait::invalid_argt( - "kbd/set-user-data", - "symbol", - &format!("{k:?}"), - )); - }; - println!("setting user data {k:?} to {v:?}"); - if let Some(mut val) = (*ctx1) - .borrow_mut() - .user_data - .insert(k.into_owned(), v.clone()) - { - // FIXME: theoretically this can unref data that's referenced elsewhere - val.unref(vm); - } - v.add_ref(vm); - Ok(Value::Null) - }), - ); - let ctx1 = ctx.clone(); - engine.register_func( - "kbd/get-user-data", - Box::new(move |_vm, args| { - let Ok::<[Value<_>; 1], _>([k]) = args.try_into() else { - return Err(ErrorTrait::invalid_argc("kbd/get-user-data")); - }; - let Value::Symbol(k) = k else { - return Err(ErrorTrait::invalid_argt( - "kbd/get-user-data", - "symbol", - &format!("{k:?}"), - )); - }; - println!("getting user data {k:?}"); - Ok((*ctx1) - .borrow() - .user_data - .get(k.as_ref()) - .cloned() - .unwrap_or(Value::Bool(false))) - }), - ); - fn add_to_i64(name: &str, a: Value, b: Value) -> Result { - Ok(match (a, b) { - (Value::Int(a), Value::Int(b)) => a.saturating_add(b), - (Value::Float(a), Value::Float(b)) => (a + b) as i64, - (Value::Int(a), Value::Float(b)) => (a as f64 + b) as i64, - (Value::Float(a), Value::Int(b)) => (a + b as f64) as i64, - x => { - return Err(E::Error::invalid_argt( - name, - "int or float", - &format!("{:?}", x.1), - )) - } - }) - } - fn to_i64(name: &str, x: Value) -> Result { - add_to_i64(name, x, Value::Int(0)) - } - let ctx1 = ctx.clone(); - engine.register_func( - "kbd/make-text", - Box::new(move |_vm, args| { - let Ok::<[Value<_>; 5], _>([xy, wh, text, font, font_size]) = args.try_into() else { - return Err(ErrorTrait::invalid_argc("kbd/make-text")); - }; - let Value::Pair(xy) = xy else { - return Err(ErrorTrait::invalid_argt( - "kbd/make-text", - "pair", - &format!("{xy:?}"), - )); - }; - let Value::Pair(wh) = wh else { - return Err(ErrorTrait::invalid_argt( - "kbd/make-text", - "pair", - &format!("{wh:?}"), - )); - }; - let Value::String(text) = text else { - return Err(ErrorTrait::invalid_argt( - "kbd/make-text", - "string", - &format!("{text:?}"), - )); - }; - let Value::String(font) = font else { - return Err(ErrorTrait::invalid_argt( - "kbd/make-text", - "string", - &format!("{font:?}"), - )); - }; - let font_size = match font_size { - Value::Int(x) => x as f64, - Value::Float(x) => x, - _ => { - return Err(ErrorTrait::invalid_argt( - "kbd/make-text", - "string", - &format!("{font_size:?}"), - )) - } - }; - let Some(font_handle) = ctx1.borrow_mut().font_db.font_handle(&font) else { - return Err(ErrorTrait::from_str("kbd/make-text", "font not found")); - }; - let (x, y) = *xy; - let (w, h) = *wh; - let x1 = to_i64("kbd/make-text", x.clone())?; - let y1 = to_i64("kbd/make-text", y.clone())?; - let x_w = add_to_i64("kbd/make-text", x, w)?; - let y_h = add_to_i64("kbd/make-text", y, h)?; - let (x, y) = (x1, y1); - let (w, h) = (x_w - x, y_h - y); - let halign = graphics::Alignment::Middle; - let valign = graphics::Alignment::Middle; - let direction = swash::shape::Direction::LeftToRight; - let script = swash::text::Script::Latin; - let color = image::ColorRgba([255, 0, 0, 255]); - // [xy, wh, text, font, font_size] - // println!("rendering text {text} of size {w}/{h} at {x}/{y} with font {font} {font_size}, color {color:?}, alignment {halign:?}/{valign:?}, script {script:?}, direction {direction:?}"); - let text = graphics::WidgetEnum::Text(graphics::Text { - direction, - font_size: font_size as f32, - font_handle, - color, - halign, - valign, - script, - text: text.into(), - pos: graphics::Rect { - left: x as i32, - bottom: y as i32, - height: h as u32, - width: w as u32, - }, - }); - Value::from_serde(&text) - .map_err(|err| ErrorTrait::from_str("kbd/make-text", &err.to_string())) - }), - ); - engine.register(":round-corners", Value::new_symbol(":round-corners")); - engine.register_func( - "kbd/make-rect", - Box::new(|_vm, args| { - let mut args = args.into_iter(); - let Some(xy) = args.next() else { - return Err(ErrorTrait::invalid_argc("kbd/make-rect")); - }; - let Some(wh) = args.next() else { - return Err(ErrorTrait::invalid_argc("kbd/make-rect")); - }; - let mut round = 0.0f64; - while let Some(arg) = args.next() { - match arg { - Value::Symbol(x) if x == ":round-corners" => { - round = 1.0; - if let Some(arg) = args.next() { - let Value::Float(arg) = arg else { - return Err(ErrorTrait::invalid_argt( - "kbd/make-rect", - "float", - &format!("{arg:?}"), - )); - }; - round = arg; - } - } - x => { - return Err(ErrorTrait::invalid_argt( - "kbd/make-rect", - "make-rect keyword arguments", - &format!("{x:?}"), - )) - } - } - } - let Value::Pair(xy) = xy else { - return Err(ErrorTrait::invalid_argt( - "kbd/make-rect", - "pair", - &format!("{xy:?}"), - )); - }; - let Value::Pair(wh) = wh else { - return Err(ErrorTrait::invalid_argt( - "kbd/make-rect", - "pair", - &format!("{wh:?}"), - )); - }; - let (x, y) = *xy; - let (w, h) = *wh; - let x1 = to_i64("kbd/make-rect", x.clone())?; - let y1 = to_i64("kbd/make-rect", y.clone())?; - let x_w = add_to_i64("kbd/make-rect", x, w)?; - let y_h = add_to_i64("kbd/make-rect", y, h)?; - let (x, y) = (x1, y1); - let (w, h) = (x_w - x, y_h - y); - let color = image::ColorRgba([0, 0, 0, 255]); - // [xy, wh, text, font, font_size] - println!( - "rendering rect of size {w}/{h} at {x}/{y} with rounding {round}, color {color:?}" - ); - let rect = graphics::WidgetEnum::Shape(graphics::SimpleShape { - col: color, - pos: graphics::Rect { - left: x as i32, - bottom: y as i32, - width: w as u32, - height: h as u32, - }, - round_corners_ratio: round as f32, - }); - Value::from_serde(&rect) - .map_err(|err| ErrorTrait::from_str("kbd/make-rect", &err.to_string())) - }), - ); - engine.register_func( - "kbd/make-layout", - Box::new(|_vm, args| { - let Ok::<[Value<_>; 1], _>([items]) = args.try_into() else { - return Err(ErrorTrait::invalid_argc("kbd/make-layout")); - }; - let Value::RevArray(items) = items else { - return Err(ErrorTrait::invalid_argt( - "kbd/make-layout", - "list", - &format!("{items:?}"), - )); - }; - let mut layout = graphics::Layout::new(); - for item in items.into_iter().rev() { - let Value::RevArray(mut item) = item else { - return Err(ErrorTrait::invalid_argt( - "kbd/make-layout", - "list of lists", - &format!("list with {item:?}"), - )); - }; - let Some(Value::String(a)) = item.pop() else { - return Err(ErrorTrait::from_str( - "kbd/make-layout", - "each item's first element must be the item id", - )); - }; - let item = Value::RevArray(item) - .to_serde() - .map_err(|err| ErrorTrait::from_str("kbd/make-layout", &err.to_string()))?; - // println!("{item:#?}"); - layout.add_child(item, &a, None); - } - Value::from_serde(&layout) - .map_err(|err| ErrorTrait::from_str("kbd/make-layout", &err.to_string())) - }), - ); - let ctx1 = ctx.clone(); - engine.register_func( - "kbd/width", - Box::new(move |_vm, args| { - let Ok::<[Value<_>; 0], _>([]) = args.try_into() else { - return Err(ErrorTrait::invalid_argc("kbd/width")); - }; - println!("getting keyboard width"); - Ok(Value::Int(ctx1.borrow().width.into())) - }), - ); - let ctx1 = ctx.clone(); - engine.register_func( - "kbd/load-font", - Box::new(move |_vm, args| { - let Ok::<[Value<_>; 1], _>([path]) = args.try_into() else { - return Err(ErrorTrait::invalid_argc("kbd/load-font")); - }; - let Value::String(path) = path else { - return Err(ErrorTrait::invalid_argt( - "kbd/load-font", - "string", - &format!("{path:?}"), - )); - }; - println!("loading font {path:?}"); - (*ctx1) - .borrow_mut() - .font_db - .load_font(path) - .map(|()| Value::Null) - .map_err(|err| ErrorTrait::from_str("kbd/load-font", &err.to_string())) - }), - ); - let ctx1 = ctx.clone(); - engine.register_func( - "kbd/set-layout", - Box::new(move |_vm, args| { - let Ok::<[Value<_>; 1], _>([layout]) = args.try_into() else { - return Err(ErrorTrait::invalid_argc("kbd/set-layout")); - }; - let layout: graphics::Layout = layout - .to_serde() - .map_err(|err| ErrorTrait::from_str("kbd/set-layout", &err.to_string()))?; - (*ctx1).borrow_mut().layout = Some(layout); - Ok(Value::Null) - }), - ); - engine.gc(); - ScriptEnv(engine, ctx) -} - -fn assert_type(_: &T, _: &T) {} - fn main() { env_logger::init(); - let mut env = new_script_env(); - // engine.gc(); + let engine = script::create_engine(); + let mut kbd: Keyboard<_> = Keyboard::new(engine); let s = std::fs::read_to_string("tmp.scm").unwrap(); - let mut kbd = Keyboard::new(); - assert_type(&kbd, &(*env.1.borrow())); - env.eval(&mut kbd, &s).unwrap(); - kbd.env = Some(env); + kbd.eval_t(&s).unwrap(); #[cfg(feature = "wayland")] if std::env::var_os("WAYLAND_DISPLAY").is_some() { diff --git a/src/script.rs b/src/script.rs index 8a84045..e95cc3a 100644 --- a/src/script.rs +++ b/src/script.rs @@ -42,6 +42,22 @@ impl Clone for Value { } impl Value { + pub fn convert(self) -> Value + where + R::Func: Func::BasicValue>, + { + match self { + Self::Null => Value::Null, + Self::Bool(x) => Value::Bool(x), + Self::Int(x) => Value::Int(x), + Self::Float(x) => Value::Float(x), + Self::String(x) => Value::String(x), + Self::Symbol(x) => Value::Symbol(x), + Self::RevArray(x) => Value::RevArray(x.into_iter().map(Self::convert).collect()), + Self::Pair(x) => Value::new_pair(x.0.convert(), x.1.convert()), + Self::Func(x) => Value::Func(x.convert::()), + } + } pub fn new_symbol(s: impl Into>) -> Self { Self::Symbol(s.into()) } @@ -168,24 +184,16 @@ impl Hash for Value { } } -pub trait ToVal { - type Engine: Engine; - fn to_val2(&self, _engine: &Self::Engine) -> Value { - self.to_val() - } - fn to_val(&self) -> Value { - unimplemented!() - } -} - pub trait ErrorTrait { fn from_str(name: &str, s: &str) -> Self; fn invalid_argc(name: &str) -> Self; fn invalid_argt(name: &str, exp: &str, found: &str) -> Self; } +pub type Args = Vec>; + pub trait Engine: Debug + Sized { - type Value: FromVal + ToVal; + type Value: ValTrait; type Error: Debug + ErrorTrait; type Func: Clone + Debug + Ord + Func; fn eval_t(&mut self, text: &str) -> Result, Self::Error>; @@ -194,32 +202,40 @@ pub trait Engine: Debug + Sized { // this doesnt have to be box, but it being a box saves some binary size fn new_func( &mut self, - name: &'static str, - func: Box< - dyn 'static + Fn(&mut Self, Vec>) -> Result, Self::Error>, - >, - ) -> Self::Func; + _name: &'static str, + _func: Box) -> Result, Self::Error>>, + ) -> Self::Func { + unimplemented!() + } + #[inline(always)] fn register_func( &mut self, name: &'static str, - func: Box< - dyn 'static + Fn(&mut Self, Vec>) -> Result, Self::Error>, - >, + func: impl 'static + Fn(&mut Self, Args) -> Result, Self::Error>, ) { - let func = self.new_func(name, func); + let func = self.new_func(name, Box::new(func)); self.register(name, Value::Func(func)); } } -pub trait Func { +pub trait Func: Sized { type Engine: Engine; + type BasicValue; fn call( &self, engine: &mut Self::Engine, - args: Vec>, + args: Args, ) -> Result, ::Error>; fn add_ref(&mut self, engine: &mut Self::Engine); fn unref(&mut self, engine: &mut Self::Engine); + fn from_basic_value(v: Self::BasicValue) -> Self; + fn into_basic_value(self) -> Self::BasicValue; + fn convert(self) -> R::Func + where + R::Func: Func, + { + R::Func::from_basic_value(self.into_basic_value()) + } } #[derive(Debug)] @@ -288,10 +304,17 @@ impl PartialOrd for MarwoodFunc { impl Func for MarwoodFunc { type Engine = marwood::vm::Vm; + type BasicValue = Self; + fn into_basic_value(self) -> Self::BasicValue { + self + } + fn from_basic_value(v: Self::BasicValue) -> Self { + v + } fn call( &self, engine: &mut Self::Engine, - args: Vec>, + args: Args, ) -> Result, ::Error> { use marwood::vm::{lambda::Lambda, opcode::OpCode, vcell::VCell}; let (lambda, is_cont) = match self { @@ -437,15 +460,13 @@ impl Engine for marwood::vm::Vm { fn register(&mut self, name: &str, val: Value) { let symbol = self.heap.put(marwood::vm::vcell::VCell::symbol(name)); let slot = self.globenv.get_binding(symbol.as_ptr().unwrap()); - let val = FromVal::from_val2(val, self); + let val = ValTrait::from_val2(val, self); self.globenv.put_slot(slot, val); } fn new_func( &mut self, name: &'static str, - func: Box< - dyn 'static + Fn(&mut Self, Vec>) -> Result, Self::Error>, - >, + func: Box) -> Result, Self::Error>>, ) -> Self::Func { let func = marwood::vm::vcell::VCell::builtin(name, move |vm| { // log::info!("calling {name}"); @@ -457,7 +478,7 @@ impl Engine for marwood::vm::Vm { args.reverse(); // log::info!("calling! {name}"); match func(vm, args) { - Ok(x) => Ok(FromVal::from_val2(x, vm)), + Ok(x) => Ok(ValTrait::from_val2(x, vm)), Err(err) => Err(err), } // log::info!("exiting {name}"); @@ -472,8 +493,14 @@ impl Engine for marwood::vm::Vm { } } -pub trait FromVal: Sized { +pub trait ValTrait: Sized { type Engine: Engine; + fn to_val2(&self, _engine: &Self::Engine) -> Value { + self.to_val() + } + fn to_val(&self) -> Value { + unimplemented!() + } fn from_val(_val: Value) -> Self { unimplemented!() } @@ -483,7 +510,7 @@ pub trait FromVal: Sized { } #[cfg(feature = "steel")] -impl FromVal for steel::SteelVal { +impl ValTrait for steel::SteelVal { type Engine = steel::steel_vm::engine::Engine; fn from_val(val: Value) -> Self { match val { @@ -512,7 +539,7 @@ impl FromVal for steel::SteelVal { } } -impl FromVal for marwood::vm::vcell::VCell { +impl ValTrait for marwood::vm::vcell::VCell { type Engine = marwood::vm::Vm; fn from_val2(val: Value, engine: &mut Self::Engine) -> Self { match val { @@ -568,10 +595,6 @@ impl FromVal for marwood::vm::vcell::VCell { }, } } -} - -impl ToVal for marwood::vm::vcell::VCell { - type Engine = marwood::vm::Vm; fn to_val2(&self, engine: &Self::Engine) -> Value { match self { Self::Bool(val) => Value::Bool(*val), diff --git a/src/text.rs b/src/text.rs index 95dcbc0..a57b2be 100644 --- a/src/text.rs +++ b/src/text.rs @@ -1,5 +1,6 @@ use std::{ collections::HashMap, + fmt::Debug, hash::Hash, io, path::Path, @@ -22,11 +23,6 @@ impl CacheKey { static KEY: AtomicU64 = AtomicU64::new(1); Self(KEY.fetch_add(1, Ordering::Relaxed)) } - - /// Returns the underlying value of the key. - pub fn value(self) -> u64 { - self.0 - } } impl Default for CacheKey { @@ -69,6 +65,13 @@ pub struct FontDb { fonts: HashMap, glyph_cache: HashMap)>, } +impl Debug for FontDb { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("FontDb") + .field("fonts", &self.fonts) + .finish_non_exhaustive() + } +} impl FontDb { pub fn load_font(&mut self, file: impl AsRef) -> io::Result<()> {