Expose structs to Lua and write Lua type annotations #28
File diff suppressed because it is too large
Load Diff
|
@ -4,3 +4,5 @@ pub use plugin::*;
|
|||
|
||||
mod window;
|
||||
pub use window::*;
|
||||
|
||||
pub use winit::dpi as dpi;
|
|
@ -13,6 +13,9 @@ use winit::{
|
|||
window::{Window, WindowAttributes, WindowId},
|
||||
};
|
||||
|
||||
pub use winit::event::{DeviceId, DeviceEvent, MouseScrollDelta, ElementState};
|
||||
pub use winit::keyboard::PhysicalKey;
|
||||
|
||||
use crate::{
|
||||
game::{App, WindowState},
|
||||
plugin::Plugin,
|
||||
|
@ -26,9 +29,9 @@ use super::WindowOptions;
|
|||
#[derive(Debug, Clone, Reflect)]
|
||||
pub struct DeviceEventPair {
|
||||
#[reflect(skip)]
|
||||
pub device_src: winit::event::DeviceId,
|
||||
pub device_src: DeviceId,
|
||||
#[reflect(skip)]
|
||||
pub event: winit::event::DeviceEvent,
|
||||
pub event: DeviceEvent,
|
||||
}
|
||||
|
||||
pub struct WinitPlugin {
|
||||
|
|
|
@ -10,6 +10,7 @@ use crate::{field::{Field, FieldType}, FN_NAME_INTERNAL_REFLECT, FN_NAME_INTERNA
|
|||
enum SkipType {
|
||||
/// Skips implementing
|
||||
LuaReflect,
|
||||
LuaWrapper,
|
||||
}
|
||||
|
||||
impl syn::parse::Parse for SkipType {
|
||||
|
@ -19,6 +20,7 @@ impl syn::parse::Parse for SkipType {
|
|||
|
||||
match name_str.as_str() {
|
||||
"lua_reflect" => Ok(Self::LuaReflect),
|
||||
"lua_wrapper" => Ok(Self::LuaWrapper),
|
||||
_ => Err(syn::Error::new_spanned(name, "unknown skip type")),
|
||||
}
|
||||
}
|
||||
|
@ -483,6 +485,24 @@ pub fn wrap_lua_struct_impl(input: proc_macro::TokenStream) -> proc_macro::Token
|
|||
}
|
||||
};
|
||||
|
||||
let lua_wrapper = if input.skips.contains(&SkipType::LuaWrapper) {
|
||||
quote!()
|
||||
} else {
|
||||
quote! {
|
||||
impl lyra_scripting::lua::LuaWrapper for #wrapper_typename {
|
||||
type Wrap = #path;
|
||||
|
||||
fn wrapped_type_id() -> std::any::TypeId {
|
||||
std::any::TypeId::of::<#path>()
|
||||
}
|
||||
|
||||
fn into_wrapped(self) -> Self::Wrap {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
for field in input.auto_fields.iter() {
|
||||
if field.field_ty.is_unknown() && !field.skip_setter {
|
||||
return syn::Error::new(
|
||||
|
@ -583,6 +603,7 @@ pub fn wrap_lua_struct_impl(input: proc_macro::TokenStream) -> proc_macro::Token
|
|||
impl mlua::UserData for #wrapper_typename {
|
||||
fn add_fields<F: mlua::UserDataFields<Self>>(fields: &mut F) {
|
||||
use mlua::IntoLua;
|
||||
use mlua::FromLua;
|
||||
|
||||
#(#fields)*
|
||||
|
||||
|
@ -591,6 +612,7 @@ pub fn wrap_lua_struct_impl(input: proc_macro::TokenStream) -> proc_macro::Token
|
|||
|
||||
fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
|
||||
use mlua::IntoLua;
|
||||
use mlua::FromLua;
|
||||
|
||||
#lua_reflects
|
||||
|
||||
|
@ -600,16 +622,6 @@ pub fn wrap_lua_struct_impl(input: proc_macro::TokenStream) -> proc_macro::Token
|
|||
}
|
||||
}
|
||||
|
||||
impl lyra_scripting::lua::LuaWrapper for #wrapper_typename {
|
||||
type Wrap = #path;
|
||||
|
||||
fn wrapped_type_id() -> std::any::TypeId {
|
||||
std::any::TypeId::of::<#path>()
|
||||
}
|
||||
|
||||
fn into_wrapped(self) -> Self::Wrap {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
#lua_wrapper
|
||||
})
|
||||
}
|
||||
|
|
|
@ -12,7 +12,11 @@ pub trait LuaWrapper {
|
|||
type Wrap: Reflect + 'static;
|
||||
|
||||
/// The type id of the wrapped type.
|
||||
fn wrapped_type_id() -> TypeId;
|
||||
#[inline(always)]
|
||||
fn wrapped_type_id() -> TypeId {
|
||||
TypeId::of::<Self::Wrap>()
|
||||
}
|
||||
|
||||
fn into_wrapped(self) -> Self::Wrap;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,318 @@
|
|||
use crate::lua::LuaWrapper;
|
||||
use lyra_game::{
|
||||
input::{KeyCode, NativeKeyCode},
|
||||
math::Vec2,
|
||||
winit::{
|
||||
dpi::PhysicalPosition, DeviceEvent, DeviceEventPair, DeviceId, MouseScrollDelta,
|
||||
PhysicalKey,
|
||||
},
|
||||
};
|
||||
use lyra_scripting_derive::wrap_lua_struct;
|
||||
|
||||
use crate::lyra_engine;
|
||||
|
||||
use super::LuaVec2;
|
||||
|
||||
wrap_lua_struct!(DeviceId, skip(lua_reflect, lua_wrapper));
|
||||
|
||||
/// Wraps [`DeviceEvent`] and implements [`IntoLua`]
|
||||
#[derive(Clone)]
|
||||
pub struct LuaDeviceEventRaw(pub(crate) DeviceEvent);
|
||||
|
||||
impl std::ops::Deref for LuaDeviceEventRaw {
|
||||
type Target = DeviceEvent;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::DerefMut for LuaDeviceEventRaw {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl mlua::FromLua for LuaDeviceEventRaw {
|
||||
fn from_lua(v: mlua::Value, _: &mlua::Lua) -> mlua::Result<Self> {
|
||||
let ty = v.type_name();
|
||||
let table = v.as_table().ok_or(mlua::Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: "LuaDeviceEventRaw".into(),
|
||||
message: Some("expected Lua Table".into()),
|
||||
})?;
|
||||
|
||||
let type_str: String = table.get("type")?;
|
||||
let type_str = type_str.as_str();
|
||||
|
||||
let ret = match type_str {
|
||||
"added" => Self(DeviceEvent::Added),
|
||||
"removed" => Self(DeviceEvent::Removed),
|
||||
"mouse_motion" => {
|
||||
let delta: LuaVec2 = table.get("delta")?;
|
||||
Self(DeviceEvent::MouseMotion {
|
||||
delta: (delta.x as _, delta.y as _),
|
||||
})
|
||||
}
|
||||
"mouse_wheel" => {
|
||||
let delta = if table.contains_key("line_delta")? {
|
||||
let ld: LuaVec2 = table.get("line_delta")?;
|
||||
MouseScrollDelta::LineDelta(ld.x, ld.y)
|
||||
} else if table.contains_key("pixel_delta")? {
|
||||
let pd: LuaVec2 = table.get("pixel_delta")?;
|
||||
let pos = PhysicalPosition::new(pd.x as f64, pd.y as f64);
|
||||
MouseScrollDelta::PixelDelta(pos)
|
||||
} else {
|
||||
return Err(mlua::Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: "LuaDeviceEventRaw".into(),
|
||||
message: Some(
|
||||
"could not find line_delta or pixel_delta in 'mouse_wheel' \
|
||||
device event"
|
||||
.into(),
|
||||
),
|
||||
});
|
||||
};
|
||||
|
||||
Self(DeviceEvent::MouseWheel { delta })
|
||||
}
|
||||
"motion" => {
|
||||
let axis: u32 = table.get("axis")?;
|
||||
let value: f64 = table.get("value")?;
|
||||
Self(DeviceEvent::Motion { axis, value })
|
||||
}
|
||||
"button" => {
|
||||
let button: u32 = table.get("button")?;
|
||||
|
||||
let state_str: String = table.get("state")?;
|
||||
let state_str = state_str.as_str();
|
||||
let state = match state_str {
|
||||
"pressed" => lyra_game::winit::ElementState::Pressed,
|
||||
"released" => lyra_game::winit::ElementState::Released,
|
||||
_ => {
|
||||
return Err(mlua::Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: "LuaDeviceEventRaw".into(),
|
||||
message: Some(format!("unknown button state: '{}'", state_str)),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
Self(DeviceEvent::Button { button, state })
|
||||
}
|
||||
"key" => {
|
||||
let state_str: String = table.get("state")?;
|
||||
let state_str = state_str.as_str();
|
||||
let state = match state_str {
|
||||
"pressed" => lyra_game::winit::ElementState::Pressed,
|
||||
"released" => lyra_game::winit::ElementState::Released,
|
||||
_ => {
|
||||
return Err(mlua::Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: "LuaDeviceEventRaw".into(),
|
||||
message: Some(format!("unknown key state: '{}'", state_str)),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
if table.contains_key("code")? {
|
||||
let code_str: String = table.get("code")?;
|
||||
let code = KeyCode::from_str(&code_str);
|
||||
if code.is_none() {
|
||||
return Err(mlua::Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: "LuaDeviceEventRaw".into(),
|
||||
message: Some(format!("unknown key code: '{}'", code_str)),
|
||||
});
|
||||
}
|
||||
|
||||
let phy = PhysicalKey::Code(code);
|
||||
Self(DeviceEvent::Key(RawKeyEvent {
|
||||
physical_key: phy,
|
||||
state,
|
||||
}))
|
||||
} else if table.contains_key("native")? {
|
||||
let native_str: String = table.get("native")?;
|
||||
let native_str = native_str.as_str();
|
||||
let native_code: u32 = table.get("native_code")?;
|
||||
|
||||
let native_code = match native_str {
|
||||
"unknown" => NativeKeyCode::Unidentified,
|
||||
"android" => NativeKeyCode::Android(native_code),
|
||||
"macos" => NativeKeyCode::MacOS(native_code as u16),
|
||||
"windows" => NativeKeyCode::Windows(native_code),
|
||||
"xkb" => NativeKeyCode::Xkb(native_code),
|
||||
};
|
||||
|
||||
Self(DeviceEvent::Key(RawKeyEvent {
|
||||
physical_key: PhysicalKey::Unidentified(native_code),
|
||||
state,
|
||||
}))
|
||||
} else {
|
||||
return Err(mlua::Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: "LuaDeviceEventRaw".into(),
|
||||
message: Some(
|
||||
"malformed Table for LuaDeviceEventRaw, missing key \
|
||||
code"
|
||||
.into(),
|
||||
),
|
||||
});
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
return Err(mlua::Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: "LuaDeviceEventRaw".into(),
|
||||
message: Some(format!("unknown device event type '{}'", type_str)),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
Ok(ret)
|
||||
}
|
||||
}
|
||||
|
||||
impl mlua::IntoLua for LuaDeviceEventRaw {
|
||||
fn into_lua(self, lua: &mlua::Lua) -> mlua::Result<mlua::Value> {
|
||||
let table = lua.create_table()?;
|
||||
|
||||
match self.0 {
|
||||
DeviceEvent::Added => {
|
||||
table.set("type", "added")?;
|
||||
}
|
||||
DeviceEvent::Removed => {
|
||||
table.set("type", "removed")?;
|
||||
}
|
||||
DeviceEvent::MouseMotion { delta } => {
|
||||
table.set("type", "mouse_motion")?;
|
||||
table.set("delta", LuaVec2(Vec2::new(delta.0 as _, delta.1 as _)))?;
|
||||
}
|
||||
DeviceEvent::MouseWheel { delta } => {
|
||||
table.set("type", "mouse_wheel")?;
|
||||
|
||||
match delta {
|
||||
MouseScrollDelta::LineDelta(x, y) => {
|
||||
let d = LuaVec2(Vec2::new(x, y));
|
||||
table.set("line_delta", d)?;
|
||||
}
|
||||
MouseScrollDelta::PixelDelta(delta) => {
|
||||
let d = delta.cast::<f32>();
|
||||
let d = LuaVec2(Vec2::new(d.x, d.y));
|
||||
table.set("pixel_delta", d)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
DeviceEvent::Motion { axis, value } => {
|
||||
table.set("type", "motion")?;
|
||||
table.set("axis", axis)?;
|
||||
table.set("value", value)?;
|
||||
}
|
||||
DeviceEvent::Button { button, state } => {
|
||||
table.set("type", "button")?;
|
||||
table.set("button", button)?;
|
||||
|
||||
let state = match state {
|
||||
lyra_game::winit::ElementState::Pressed => "pressed",
|
||||
lyra_game::winit::ElementState::Released => "released",
|
||||
};
|
||||
table.set("state", state)?;
|
||||
}
|
||||
DeviceEvent::Key(raw_key_event) => {
|
||||
table.set("type", "key")?;
|
||||
|
||||
let state = match raw_key_event.state {
|
||||
lyra_game::winit::ElementState::Pressed => "pressed",
|
||||
lyra_game::winit::ElementState::Released => "released",
|
||||
};
|
||||
table.set("state", state)?;
|
||||
|
||||
let e = KeyCode::from_raw_event(&raw_key_event);
|
||||
let s = e.as_str();
|
||||
|
||||
if let Some(s) = s {
|
||||
table.set("code", s)?;
|
||||
} else if let Some(un) = e.get_unknown() {
|
||||
match un {
|
||||
lyra_game::input::NativeKeyCode::Unidentified => {
|
||||
table.set("native", "unknown")?;
|
||||
}
|
||||
lyra_game::input::NativeKeyCode::Android(v) => {
|
||||
table.set("native", "android")?;
|
||||
table.set("native_code", *v)?;
|
||||
}
|
||||
lyra_game::input::NativeKeyCode::MacOS(v) => {
|
||||
table.set("native", "macos")?;
|
||||
table.set("native_code", *v)?;
|
||||
}
|
||||
lyra_game::input::NativeKeyCode::Windows(v) => {
|
||||
table.set("native", "windows")?;
|
||||
table.set("native_code", *v)?;
|
||||
}
|
||||
lyra_game::input::NativeKeyCode::Xkb(v) => {
|
||||
table.set("native", "xkb")?;
|
||||
table.set("native_code", *v)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
table.into_lua(lua)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct LuaDeviceEvent(pub(crate) DeviceEventPair);
|
||||
|
||||
impl std::ops::Deref for LuaDeviceEvent {
|
||||
type Target = DeviceEventPair;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::DerefMut for LuaDeviceEvent {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl mlua::FromLua for LuaDeviceEvent {
|
||||
fn from_lua(v: mlua::Value, _: &mlua::Lua) -> mlua::Result<Self> {
|
||||
let ty = v.type_name();
|
||||
let table = v.as_table().ok_or(mlua::Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: "LuaDeviceEvent".into(),
|
||||
message: Some("expected Lua Table".into()),
|
||||
})?;
|
||||
|
||||
let id: LuaDeviceId = table.get("device")?;
|
||||
let ev: LuaDeviceEventRaw = table.get("event")?;
|
||||
|
||||
Ok(Self(DeviceEventPair {
|
||||
device_src: id,
|
||||
event: ev,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
impl mlua::IntoLua for LuaDeviceEvent {
|
||||
fn into_lua(self, lua: &mlua::Lua) -> mlua::Result<mlua::Value> {
|
||||
let table = lua.create_table()?;
|
||||
table.set("device", LuaDeviceId(self.device_src))?;
|
||||
table.set("event", LuaDeviceEventRaw(self.event.clone()))?;
|
||||
|
||||
table.into_lua(lua)
|
||||
}
|
||||
}
|
||||
|
||||
impl LuaWrapper for LuaDeviceEvent {
|
||||
type Wrap = DeviceEventPair;
|
||||
|
||||
#[inline(always)]
|
||||
fn into_wrapped(self) -> Self::Wrap {
|
||||
self.0
|
||||
}
|
||||
}
|
|
@ -18,3 +18,6 @@ pub use camera::*;
|
|||
|
||||
mod free_fly_camera;
|
||||
pub use free_fly_camera::*;
|
||||
|
||||
mod events;
|
||||
pub use events::*;
|
Loading…
Reference in New Issue