scheme support refactoring
This commit is contained in:
parent
01e0871111
commit
dfcc06fb31
|
@ -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,
|
||||
}
|
||||
|
|
205
src/keyboard.rs
205
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<E: Engine> {
|
||||
pub supers: Vec<String>,
|
||||
pub methods: HashMap<String, E::Func>,
|
||||
|
@ -43,18 +47,24 @@ pub struct Keyboard<E: Engine> {
|
|||
fmt: PixelFormat,
|
||||
pub(crate) font_db: FontDb,
|
||||
is_first_draw: bool,
|
||||
config_handlers: HashMap<
|
||||
String,
|
||||
Vec<(
|
||||
usize,
|
||||
Box<dyn 'static + FnMut(&Self, &str, &toml_edit::Value)>,
|
||||
)>,
|
||||
>,
|
||||
timers: Vec<(Instant, Box<dyn 'static + FnOnce(&Self)>)>,
|
||||
config_handler_id: usize,
|
||||
pub(crate) user_data: HashMap<String, Value<E>>,
|
||||
pub(crate) classes: HashMap<String, Class<E>>,
|
||||
pub(crate) env: Option<crate::ScriptEnv<E>>,
|
||||
pub(crate) user_data: HashMap<String, Value<Self>>,
|
||||
pub(crate) classes: HashMap<String, Class<Self>>,
|
||||
/// SAFETY: unsafe
|
||||
engine: E,
|
||||
}
|
||||
impl<E: Engine> Debug for Keyboard<E> {
|
||||
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<E: Engine> Default for Keyboard<E> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Engine> Keyboard<E> {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
impl<E: 'static + Engine> Keyboard<E> {
|
||||
pub fn new(engine: E) -> Self {
|
||||
let mut ret = Self {
|
||||
width: 720,
|
||||
height: 480,
|
||||
font_db: Default::default(),
|
||||
|
@ -143,16 +147,16 @@ impl<E: Engine> Keyboard<E> {
|
|||
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<E: Engine> Keyboard<E> {
|
||||
pub fn size(&self) -> (u32, u32) {
|
||||
(self.width, self.height)
|
||||
}
|
||||
|
@ -189,24 +193,6 @@ impl<E: Engine> Keyboard<E> {
|
|||
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<E: Engine> Keyboard<E> {
|
|||
.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<E: Engine> Keyboard<E> {
|
|||
layout
|
||||
}*/
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct ValueWrapper<E: Engine>(E::Value);
|
||||
impl<E: Engine> ValTrait for ValueWrapper<E> {
|
||||
type Engine = Keyboard<E>;
|
||||
fn from_val2(val: Value<Self::Engine>, engine: &mut Self::Engine) -> Self {
|
||||
Self(E::Value::from_val2(val.convert(), &mut engine.engine))
|
||||
}
|
||||
fn from_val(val: Value<Self::Engine>) -> Self {
|
||||
Self(E::Value::from_val(val.convert()))
|
||||
}
|
||||
fn to_val(&self) -> Value<Self::Engine> {
|
||||
self.0.to_val().convert()
|
||||
}
|
||||
fn to_val2(&self, engine: &Self::Engine) -> Value<Self::Engine> {
|
||||
self.0.to_val2(&engine.engine).convert()
|
||||
}
|
||||
}
|
||||
#[repr(transparent)]
|
||||
#[derive(Debug)]
|
||||
pub struct FuncWrapper<E: Engine>(E::Func);
|
||||
impl<E: Engine> Clone for FuncWrapper<E>
|
||||
where
|
||||
E::Func: Clone,
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
Self(self.0.clone())
|
||||
}
|
||||
}
|
||||
impl<E: Engine> Ord for FuncWrapper<E>
|
||||
where
|
||||
E::Func: Ord,
|
||||
{
|
||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||
self.0.cmp(&other.0)
|
||||
}
|
||||
}
|
||||
impl<E: Engine> Eq for FuncWrapper<E> where E::Func: Eq {}
|
||||
impl<E: Engine> PartialOrd for FuncWrapper<E>
|
||||
where
|
||||
E::Func: PartialOrd,
|
||||
{
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
Some(self.0.cmp(&other.0))
|
||||
}
|
||||
}
|
||||
impl<E: Engine> PartialEq for FuncWrapper<E>
|
||||
where
|
||||
E::Func: PartialEq,
|
||||
{
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.0.eq(&other.0)
|
||||
}
|
||||
}
|
||||
impl<E: Engine> Func for FuncWrapper<E> {
|
||||
type BasicValue = <E::Func as Func>::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<E>;
|
||||
fn call(
|
||||
&self,
|
||||
engine: &mut Self::Engine,
|
||||
args: Vec<Value<Self::Engine>>,
|
||||
) -> Result<Value<Self::Engine>, <Self::Engine as Engine>::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<E: Engine> Engine for Keyboard<E> {
|
||||
type Value = ValueWrapper<E>;
|
||||
type Error = E::Error;
|
||||
type Func = FuncWrapper<E>;
|
||||
fn eval_t(&mut self, text: &str) -> Result<Value<Self>, 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>) {
|
||||
self.engine.register(name, val.convert())
|
||||
}
|
||||
#[inline(always)]
|
||||
fn register_func(
|
||||
&mut self,
|
||||
name: &'static str,
|
||||
func: impl 'static + Fn(&mut Self, Vec<Value<Self>>) -> Result<Value<Self>, 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<Value<E>>, Vec<Value<Self>>>(b) },
|
||||
)?
|
||||
.convert())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
469
src/keyboard/script.rs
Normal file
469
src/keyboard/script.rs
Normal file
|
@ -0,0 +1,469 @@
|
|||
use crate::{
|
||||
graphics, image,
|
||||
script::{Args, Engine, ErrorTrait, Func, Value},
|
||||
};
|
||||
|
||||
use super::Keyboard;
|
||||
|
||||
fn register<E: Engine>(
|
||||
vm: &mut Keyboard<E>,
|
||||
args: Args<Keyboard<E>>,
|
||||
) -> Result<Value<Keyboard<E>>, <Keyboard<E> 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<E: Engine>(
|
||||
vm: &mut Keyboard<E>,
|
||||
args: Args<Keyboard<E>>,
|
||||
) -> Result<Value<Keyboard<E>>, <Keyboard<E> 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::<Result<Vec<_>, _>>()?;
|
||||
println!("register {cls:?} {supers:?}");
|
||||
|
||||
vm.classes
|
||||
.entry(cls.into_owned())
|
||||
.or_default()
|
||||
.supers
|
||||
.extend(supers);
|
||||
println!("done");
|
||||
Ok(Value::Null)
|
||||
}
|
||||
|
||||
fn call<E: Engine>(
|
||||
vm: &mut Keyboard<E>,
|
||||
mut args: Args<Keyboard<E>>,
|
||||
) -> Result<Value<Keyboard<E>>, <Keyboard<E> 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<E: Engine>(
|
||||
func: &mut E::Func,
|
||||
engine: &mut E,
|
||||
args: Vec<Value<E>>,
|
||||
) -> Result<Value<E>, 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<E: Engine>(
|
||||
vm: &mut Keyboard<E>,
|
||||
args: Args<Keyboard<E>>,
|
||||
) -> Result<Value<Keyboard<E>>, <Keyboard<E> 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<E: Engine>(
|
||||
vm: &mut Keyboard<E>,
|
||||
args: Args<Keyboard<E>>,
|
||||
) -> Result<Value<Keyboard<E>>, <Keyboard<E> 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<E: Engine>(name: &str, a: Value<E>, b: Value<E>) -> Result<i64, E::Error> {
|
||||
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<E: Engine>(name: &str, x: Value<E>) -> Result<i64, E::Error> {
|
||||
add_to_i64(name, x, Value::Int(0))
|
||||
}
|
||||
|
||||
fn make_text<E: Engine>(
|
||||
vm: &mut Keyboard<E>,
|
||||
args: Args<Keyboard<E>>,
|
||||
) -> Result<Value<Keyboard<E>>, <Keyboard<E> 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<E: Engine>(
|
||||
_vm: &mut Keyboard<E>,
|
||||
args: Args<Keyboard<E>>,
|
||||
) -> Result<Value<Keyboard<E>>, <Keyboard<E> 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<E: Engine>(
|
||||
_vm: &mut Keyboard<E>,
|
||||
args: Args<Keyboard<E>>,
|
||||
) -> Result<Value<Keyboard<E>>, <Keyboard<E> 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<E: Engine>(
|
||||
vm: &mut Keyboard<E>,
|
||||
args: Args<Keyboard<E>>,
|
||||
) -> Result<Value<Keyboard<E>>, <Keyboard<E> 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<E: Engine>(
|
||||
vm: &mut Keyboard<E>,
|
||||
args: Args<Keyboard<E>>,
|
||||
) -> Result<Value<Keyboard<E>>, <Keyboard<E> 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<E: Engine>(
|
||||
vm: &mut Keyboard<E>,
|
||||
args: Args<Keyboard<E>>,
|
||||
) -> Result<Value<Keyboard<E>>, <Keyboard<E> 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<E: 'static + Engine>(engine: &mut Keyboard<E>) {
|
||||
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();
|
||||
}
|
506
src/main.rs
506
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: Engine>(E, Rc<RefCell<Keyboard<E>>>);
|
||||
|
||||
impl<E: Engine> ScriptEnv<E> {
|
||||
fn eval(&mut self, kbd: &mut Keyboard<E>, text: &str) -> Result<Value<E>, 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<E>,
|
||||
func: &E::Func,
|
||||
args: Vec<Value<E>>,
|
||||
) -> Result<Value<E>, 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<impl Engine> {
|
||||
let mut engine = script::create_engine();
|
||||
const DEFS: &str = include_str!("../defs.scm");
|
||||
let ctx: Rc<RefCell<Keyboard<_>>> = 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::<Result<Vec<_>, _>>()?;
|
||||
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<E: Engine>(
|
||||
func: &mut E::Func,
|
||||
engine: &mut E,
|
||||
args: Vec<Value<E>>,
|
||||
) -> Result<Value<E>, 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<E: Engine>(name: &str, a: Value<E>, b: Value<E>) -> Result<i64, E::Error> {
|
||||
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<E: Engine>(name: &str, x: Value<E>) -> Result<i64, E::Error> {
|
||||
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, _: &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() {
|
||||
|
|
|
@ -42,6 +42,22 @@ impl<E: Engine> Clone for Value<E> {
|
|||
}
|
||||
|
||||
impl<E: Engine> Value<E> {
|
||||
pub fn convert<R: Engine>(self) -> Value<R>
|
||||
where
|
||||
R::Func: Func<BasicValue = <E::Func as 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::<R>()),
|
||||
}
|
||||
}
|
||||
pub fn new_symbol(s: impl Into<Cow<'static, str>>) -> Self {
|
||||
Self::Symbol(s.into())
|
||||
}
|
||||
|
@ -168,24 +184,16 @@ impl<E: Engine> Hash for Value<E> {
|
|||
}
|
||||
}
|
||||
|
||||
pub trait ToVal {
|
||||
type Engine: Engine;
|
||||
fn to_val2(&self, _engine: &Self::Engine) -> Value<Self::Engine> {
|
||||
self.to_val()
|
||||
}
|
||||
fn to_val(&self) -> Value<Self::Engine> {
|
||||
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<E> = Vec<Value<E>>;
|
||||
|
||||
pub trait Engine: Debug + Sized {
|
||||
type Value: FromVal<Engine = Self> + ToVal<Engine = Self>;
|
||||
type Value: ValTrait<Engine = Self>;
|
||||
type Error: Debug + ErrorTrait;
|
||||
type Func: Clone + Debug + Ord + Func<Engine = Self>;
|
||||
fn eval_t(&mut self, text: &str) -> Result<Value<Self>, 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<Value<Self>>) -> Result<Value<Self>, Self::Error>,
|
||||
>,
|
||||
) -> Self::Func;
|
||||
_name: &'static str,
|
||||
_func: Box<dyn 'static + Fn(&mut Self, Args<Self>) -> Result<Value<Self>, Self::Error>>,
|
||||
) -> Self::Func {
|
||||
unimplemented!()
|
||||
}
|
||||
#[inline(always)]
|
||||
fn register_func(
|
||||
&mut self,
|
||||
name: &'static str,
|
||||
func: Box<
|
||||
dyn 'static + Fn(&mut Self, Vec<Value<Self>>) -> Result<Value<Self>, Self::Error>,
|
||||
>,
|
||||
func: impl 'static + Fn(&mut Self, Args<Self>) -> Result<Value<Self>, 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<Value<Self::Engine>>,
|
||||
args: Args<Self::Engine>,
|
||||
) -> Result<Value<Self::Engine>, <Self::Engine as Engine>::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<R: Engine>(self) -> R::Func
|
||||
where
|
||||
R::Func: Func<BasicValue = Self::BasicValue>,
|
||||
{
|
||||
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<Value<Self::Engine>>,
|
||||
args: Args<Self::Engine>,
|
||||
) -> Result<Value<Self::Engine>, <Self::Engine as Engine>::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<Self>) {
|
||||
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<Value<Self>>) -> Result<Value<Self>, Self::Error>,
|
||||
>,
|
||||
func: Box<dyn 'static + Fn(&mut Self, Args<Self>) -> Result<Value<Self>, 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::Engine> {
|
||||
self.to_val()
|
||||
}
|
||||
fn to_val(&self) -> Value<Self::Engine> {
|
||||
unimplemented!()
|
||||
}
|
||||
fn from_val(_val: Value<Self::Engine>) -> 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<Self::Engine>, 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<Self::Engine> {
|
||||
match self {
|
||||
Self::Bool(val) => Value::Bool(*val),
|
||||
|
|
13
src/text.rs
13
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<String, FontHandle>,
|
||||
glyph_cache: HashMap<GlyphCacheKey, (Instant, Option<Image>)>,
|
||||
}
|
||||
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<Path>) -> io::Result<()> {
|
||||
|
|
Loading…
Reference in a new issue