use gizmo_core::input::Input;
use mlua::prelude::*;
pub fn register_input_api(lua: &Lua) -> Result<(), LuaError> {
crate::api_table::register_protected(lua, "input", |input_table| {
input_table.raw_set("_keys", lua.create_table()?)?;
input_table.raw_set("_just_keys", lua.create_table()?)?;
input_table.raw_set("_mouse_x", 0.0f32)?;
input_table.raw_set("_mouse_y", 0.0f32)?;
input_table.raw_set("_mouse_dx", 0.0f32)?;
input_table.raw_set("_mouse_dy", 0.0f32)?;
input_table.raw_set("_mouse_left", false)?;
input_table.raw_set("_mouse_right", false)?;
input_table.raw_set("_mouse_middle", false)?;
let key_map = lua.create_table()?;
for (name, code) in gizmo_core::input::NAMED_KEYS {
key_map.set(*name, *code)?;
}
input_table.raw_set("_key_map", key_map)?;
lua.load(
r#"
function input.is_pressed(key_name)
local code = input._key_map[string.lower(key_name)]
if code and input._keys[code] then
return true
end
return false
end
function input.is_just_pressed(key_name)
local code = input._key_map[string.lower(key_name)]
if code and input._just_keys[code] then
return true
end
return false
end
function input.mouse_position()
return { x = input._mouse_x, y = input._mouse_y }
end
function input.mouse_delta()
return { x = input._mouse_dx, y = input._mouse_dy }
end
function input.is_mouse_pressed(button)
if button == "left" then return input._mouse_left
elseif button == "right" then return input._mouse_right
elseif button == "middle" then return input._mouse_middle
end
return false
end
"#,
)
.exec()
})
}
#[tracing::instrument(skip_all, name = "script_input_read")]
pub fn update_input_api(lua: &Lua, input: &Input) -> Result<(), LuaError> {
let input_table = crate::api_table::raw(lua, "input")?;
let keys = lua.create_table()?;
let just_keys = lua.create_table()?;
for code in 0..256u32 {
if input.is_key_pressed(code) {
keys.set(code, true)?;
}
if input.is_key_just_pressed(code) {
just_keys.set(code, true)?;
}
}
input_table.raw_set("_keys", keys)?;
input_table.raw_set("_just_keys", just_keys)?;
let (mx, my) = input.mouse_position();
input_table.raw_set("_mouse_x", mx)?;
input_table.raw_set("_mouse_y", my)?;
let (dx, dy) = input.mouse_delta();
input_table.raw_set("_mouse_dx", dx)?;
input_table.raw_set("_mouse_dy", dy)?;
input_table.raw_set(
"_mouse_left",
input.is_mouse_button_pressed(gizmo_core::input::mouse::LEFT),
)?;
input_table.raw_set(
"_mouse_right",
input.is_mouse_button_pressed(gizmo_core::input::mouse::RIGHT),
)?;
input_table.raw_set(
"_mouse_middle",
input.is_mouse_button_pressed(gizmo_core::input::mouse::MIDDLE),
)?;
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use gizmo_core::input::code_from_name;
fn press(lua: &Lua, held: &[&str], just: &[&str]) {
let table = |names: &[&str]| {
let t = lua.create_table().unwrap();
for n in names {
t.raw_set(code_from_name(n).expect("known key"), true).unwrap();
}
t
};
let input_table = crate::api_table::raw(lua, "input").unwrap();
input_table.raw_set("_keys", table(held)).unwrap();
input_table.raw_set("_just_keys", table(just)).unwrap();
}
#[test]
fn n_and_w_keys_do_not_collide() {
let lua = Lua::new();
register_input_api(&lua).unwrap();
press(&lua, &["w"], &[]);
lua.load(
r#"
assert(input.is_pressed("w") == true, "w basili olmali")
assert(input.is_pressed("n") == false, "n basili OLMAMALI (w ile cakisma)")
"#,
)
.exec()
.unwrap();
press(&lua, &["n"], &[]);
lua.load(
r#"
assert(input.is_pressed("n") == true, "n kendi keycode'unda basili olmali")
assert(input.is_pressed("w") == false, "w basili OLMAMALI")
"#,
)
.exec()
.unwrap();
}
#[test]
fn the_arrow_keys_are_not_swapped() {
let lua = Lua::new();
register_input_api(&lua).unwrap();
press(&lua, &["down"], &[]);
lua.load(
r#"
assert(input.is_pressed("down") == true, "asagi basili")
assert(input.is_pressed("right") == false, "sag basili DEGIL")
assert(input.is_pressed("up") == false and input.is_pressed("left") == false)
"#,
)
.exec()
.unwrap();
press(&lua, &["right"], &[]);
lua.load(
r#"
assert(input.is_pressed("right") == true, "sag basili")
assert(input.is_pressed("down") == false, "asagi basili DEGIL")
"#,
)
.exec()
.unwrap();
}
#[test]
fn is_just_pressed_is_independent_from_held() {
let lua = Lua::new();
register_input_api(&lua).unwrap();
press(&lua, &["space"], &[]);
lua.load(
r#"
assert(input.is_pressed("space") == true, "space surekli basili")
assert(input.is_just_pressed("space") == false, "space bu frame basilmadi")
"#,
)
.exec()
.unwrap();
press(&lua, &["space"], &["space"]);
lua.load(r#"assert(input.is_just_pressed("space") == true, "space bu frame basildi")"#)
.exec()
.unwrap();
}
#[test]
fn key_name_casing_unknown_and_digits() {
let lua = Lua::new();
register_input_api(&lua).unwrap();
press(&lua, &["w", "1"], &[]);
lua.load(
r#"
assert(input.is_pressed("W") == true, "buyuk harf W basili sayilmali")
assert(input.is_pressed("w") == true, "kucuk harf w basili")
assert(input.is_pressed("1") == true, "rakam tusu 1")
assert(input.is_pressed("bilinmeyen_tus") == false, "haritada olmayan ad false")
assert(input.is_pressed("2") == false, "basilmayan rakam false")
"#,
)
.exec()
.unwrap();
}
#[test]
fn mouse_helpers_read_snapshot() {
let lua = Lua::new();
register_input_api(&lua).unwrap();
let t = crate::api_table::raw(&lua, "input").unwrap();
for (k, v) in [("_mouse_x", 120.0f32), ("_mouse_y", 45.0), ("_mouse_dx", -3.0), ("_mouse_dy", 7.0)] {
t.raw_set(k, v).unwrap();
}
for (k, v) in [("_mouse_left", true), ("_mouse_right", false), ("_mouse_middle", true)] {
t.raw_set(k, v).unwrap();
}
lua.load(
r#"
local p = input.mouse_position()
assert(p.x == 120.0 and p.y == 45.0, "pozisyon")
local d = input.mouse_delta()
assert(d.x == -3.0 and d.y == 7.0, "delta")
assert(input.is_mouse_pressed("left") == true, "sol basılı")
assert(input.is_mouse_pressed("right") == false, "sağ basılı değil")
assert(input.is_mouse_pressed("middle") == true, "orta basılı")
assert(input.is_mouse_pressed("side") == false, "bilinmeyen düğme false")
"#,
)
.exec()
.unwrap();
}
#[test]
fn update_input_api_mirrors_real_input() {
use gizmo_core::input::{mouse, Input};
let lua = Lua::new();
register_input_api(&lua).unwrap();
let mut input = Input::default();
input.on_key_pressed(code_from_name("w").unwrap());
input.set_mouse_position(200.0, 100.0);
input.on_mouse_delta(5.0, -2.0);
input.on_mouse_button_pressed(mouse::RIGHT);
update_input_api(&lua, &input).unwrap();
lua.load(
r#"
assert(input.is_pressed("w") == true, "w World'den aktarılmalı")
assert(input.is_just_pressed("w") == true, "w bu frame basıldı")
local p = input.mouse_position()
assert(p.x == 200.0 and p.y == 100.0, "fare konumu aktarılmalı")
local d = input.mouse_delta()
assert(math.abs(d.x - 5.0) < 1e-5 and math.abs(d.y + 2.0) < 1e-5, "fare delta")
assert(input.is_mouse_pressed("right") == true, "sağ tık aktarılmalı")
assert(input.is_mouse_pressed("left") == false, "sol tık basılı değil")
"#,
)
.exec()
.unwrap();
}
}