Skip to content

Commit

Permalink
Merge #279
Browse files Browse the repository at this point in the history
279: Signals now accept parameters r=Bromeon a=LeaoLuciano

Project for testing: 
- [dodge-the-creeps-signals.zip](https://github.com/godot-rust/gdext/files/11507083/dodge-the-creeps-signals.zip)
- Added a signal named test in Player scene that is emitted when player dies
- This signal is connected in Main scene which calls test_function, printing player's death position



Co-authored-by: Luciano Leão <lucianorsleao@gmail.com>
  • Loading branch information
bors[bot] and LeaoLuciano authored Jun 6, 2023
2 parents 32f0efc + 069b647 commit 7573721
Show file tree
Hide file tree
Showing 5 changed files with 164 additions and 30 deletions.
3 changes: 2 additions & 1 deletion godot-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ pub mod private {
pub use crate::registry::{callbacks, ClassPlugin, ErasedRegisterFn, PluginComponent};
pub use crate::storage::as_storage;
pub use crate::{
gdext_register_method, gdext_register_method_inner, gdext_virtual_method_callback,
gdext_get_arguments_info, gdext_register_method, gdext_register_method_inner,
gdext_virtual_method_callback,
};

use crate::{log, sys};
Expand Down
39 changes: 24 additions & 15 deletions godot-core/src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -229,20 +229,7 @@ macro_rules! gdext_register_method_inner {

// Arguments meta-information
let argument_count = NUM_ARGS as u32;
let mut arguments_info: [PropertyInfo; NUM_ARGS] = {
let mut i = -1i32;
[$(
{
i += 1;
let prop = Sig::property_info(i, stringify!($param));
//OnceArg::new(prop)
prop
},
)*]
};
let mut arguments_info_sys: [sys::GDExtensionPropertyInfo; NUM_ARGS]
= std::array::from_fn(|i| arguments_info[i].property_sys());
// = std::array::from_fn(|i| arguments_info[i].once_sys());
let mut arguments_info: [sys::GDExtensionPropertyInfo; NUM_ARGS] = $crate::gdext_get_arguments_info!(($($RetTy)+, $($ParamTy),*), $( $param, )*);
let mut arguments_metadata: [sys::GDExtensionClassMethodArgumentMetadata; NUM_ARGS]
= std::array::from_fn(|i| Sig::param_metadata(i as i32));

Expand All @@ -262,7 +249,7 @@ macro_rules! gdext_register_method_inner {
return_value_info: std::ptr::addr_of_mut!(return_value_info_sys),
return_value_metadata,
argument_count,
arguments_info: arguments_info_sys.as_mut_ptr(),
arguments_info: arguments_info.as_mut_ptr(),
arguments_metadata: arguments_metadata.as_mut_ptr(),
default_argument_count: 0,
default_arguments: std::ptr::null_mut(),
Expand Down Expand Up @@ -621,3 +608,25 @@ macro_rules! gdext_ptrcall {
);
};
}

#[doc(hidden)]
#[macro_export]
macro_rules! gdext_get_arguments_info {
(
$Signature:ty,
$($param:ident,)*
) => {
{
use $crate::builtin::meta::*;

let mut i = -1i32;
[$(
{
i += 1;
let prop = <$Signature as VarcallSignatureTuple>::property_info(i, stringify!($param)).property_sys();
prop
},
)*]
}
};
}
54 changes: 40 additions & 14 deletions godot-macros/src/godot_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use crate::util;
use crate::util::bail;
use proc_macro2::{Ident, TokenStream};
use quote::quote;
use venial::{AttributeValue, Declaration, Error, Function, Impl, ImplMember};
use venial::{AttributeValue, Declaration, Error, FnParam, Function, Impl, ImplMember, TyExpr};

pub fn transform(input_decl: Declaration) -> Result<TokenStream, Error> {
let decl = match input_decl {
Expand Down Expand Up @@ -62,19 +62,41 @@ fn transform_inherent_impl(mut decl: Impl) -> Result<TokenStream, Error> {
let class_name = util::validate_impl(&decl, None, "godot_api")?;
let class_name_str = class_name.to_string();

//let register_fn = format_ident!("__godot_rust_register_{}", class_name_str);
//#[allow(non_snake_case)]

let (funcs, signals) = process_godot_fns(&mut decl)?;
let signal_name_strs = signals.into_iter().map(|ident| ident.to_string());

let mut signal_name_strs: Vec<String> = Vec::new();
let mut signal_parameters_count: Vec<i64> = Vec::new();
let mut signal_parameters: Vec<TokenStream> = Vec::new();

for signature in signals {
let mut param_types: Vec<TyExpr> = Vec::new();
let mut param_names: Vec<Ident> = Vec::new();

for param in signature.params.inner {
match &param.0 {
FnParam::Typed(param) => {
param_types.push(param.ty.clone());
param_names.push(param.name.clone());
}
FnParam::Receiver(_) => {}
};
}

signal_name_strs.push(signature.name.to_string());
signal_parameters_count.push(param_names.len() as i64);
signal_parameters.push(
quote! {
::godot::private::gdext_get_arguments_info!(((), #(#param_types ),*), #(#param_names, )*).as_ptr()
},
);
}

let prv = quote! { ::godot::private };

let result = quote! {
#decl

impl ::godot::obj::cap::ImplementsGodotApi for #class_name {
//fn __register_methods(_builder: &mut ::godot::builder::ClassBuilder<Self>) {
fn __register_methods() {
#(
::godot::private::gdext_register_method!(#class_name, #funcs);
Expand All @@ -83,14 +105,17 @@ fn transform_inherent_impl(mut decl: Impl) -> Result<TokenStream, Error> {
unsafe {
let class_name = ::godot::builtin::StringName::from(#class_name_str);
use ::godot::sys;

#(
let parameters = #signal_parameters;
let signal_name = ::godot::builtin::StringName::from(#signal_name_strs);

sys::interface_fn!(classdb_register_extension_class_signal)(
sys::get_library(),
class_name.string_sys(),
signal_name.string_sys(),
std::ptr::null(), // NULL only valid for zero parameters, in current impl; maybe better empty slice
0,
parameters,
sys::GDExtensionInt::from(#signal_parameters_count),
);
)*
}
Expand All @@ -110,9 +135,9 @@ fn transform_inherent_impl(mut decl: Impl) -> Result<TokenStream, Error> {
Ok(result)
}

fn process_godot_fns(decl: &mut Impl) -> Result<(Vec<Function>, Vec<Ident>), Error> {
fn process_godot_fns(decl: &mut Impl) -> Result<(Vec<Function>, Vec<Function>), Error> {
let mut func_signatures = vec![];
let mut signal_idents = vec![]; // TODO consider signature
let mut signal_signatures = vec![];

let mut removed_indexes = vec![];
for (index, item) in decl.body_items.iter_mut().enumerate() {
Expand Down Expand Up @@ -147,11 +172,12 @@ fn process_godot_fns(decl: &mut Impl) -> Result<(Vec<Function>, Vec<Ident>), Err
func_signatures.push(sig);
}
BoundAttrType::Signal(ref _attr_val) => {
if !method.params.is_empty() || method.return_ty.is_some() {
return attr.bail("parameters and return types not yet supported", method);
if method.return_ty.is_some() {
return attr.bail("return types are not supported", method);
}
let sig = util::reduce_to_signature(method);

signal_idents.push(method.name.clone());
signal_signatures.push(sig.clone());
removed_indexes.push(index);
}
}
Expand All @@ -164,7 +190,7 @@ fn process_godot_fns(decl: &mut Impl) -> Result<(Vec<Function>, Vec<Ident>), Err
decl.body_items.remove(index);
}

Ok((func_signatures, signal_idents))
Ok((func_signatures, signal_signatures))
}

fn extract_attributes(method: &Function) -> Result<Option<BoundAttr>, Error> {
Expand Down
1 change: 1 addition & 0 deletions itest/rust/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ mod projection_test;
mod quaternion_test;
mod rect2i_test;
mod rid_test;
mod signal_test;
mod singleton_test;
mod string;
mod transform2d_test;
Expand Down
97 changes: 97 additions & 0 deletions itest/rust/src/signal_test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/

use std::cell::Cell;

use godot::bind::{godot_api, GodotClass};
use godot::builtin::{GodotString, Variant};

use godot::engine::Object;
use godot::obj::{Base, Gd, Share};
use godot::sys;

use crate::itest;

#[derive(GodotClass)]
#[class(init, base=Object)]
struct Emitter {
#[base]
base: Base<Object>,
}

#[godot_api]
impl Emitter {
#[signal]
fn signal_0_arg();
#[signal]
fn signal_1_arg(arg1: i64);
#[signal]
fn signal_2_arg(arg1: Gd<Object>, arg2: GodotString);
}

#[derive(GodotClass)]
#[class(init, base=Object)]
struct Receiver {
used: [Cell<bool>; 3],
#[base]
base: Base<Object>,
}

#[godot_api]
impl Receiver {
#[func]
fn receive_0_arg(&self) {
self.used[0].set(true);
}
#[func]
fn receive_1_arg(&self, arg1: i64) {
self.used[1].set(true);
assert_eq!(arg1, 987);
}
#[func]
fn receive_2_arg(&self, arg1: Gd<Object>, arg2: GodotString) {
assert_eq!(self.base.share(), arg1);
assert_eq!(SIGNAL_ARG_STRING, arg2.to_string());

self.used[2].set(true);
}
}

const SIGNAL_ARG_STRING: &str = "Signal string arg";

#[itest]
/// Test that godot can call a method that is connect with a signal
fn signals() {
let mut emitter = Gd::<Emitter>::new_default();
let receiver = Gd::<Receiver>::new_default();

let args = [
vec![],
vec![Variant::from(987)],
vec![
Variant::from(receiver.share()),
Variant::from(SIGNAL_ARG_STRING),
],
];

for (i, arg) in args.iter().enumerate() {
let signal_name = format!("signal_{i}_arg");
let receiver_name = format!("receive_{i}_arg");

emitter.bind_mut().connect(
signal_name.clone().into(),
receiver.callable(receiver_name),
0,
);

emitter.bind_mut().emit_signal(signal_name.into(), arg);

assert!(receiver.bind().used[i].get());
}

receiver.free();
emitter.free();
}

0 comments on commit 7573721

Please sign in to comment.