Skip to content

Commit 7573721

Browse files
Merge #279
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 <[email protected]>
2 parents 32f0efc + 069b647 commit 7573721

File tree

5 files changed

+164
-30
lines changed

5 files changed

+164
-30
lines changed

godot-core/src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,8 @@ pub mod private {
5353
pub use crate::registry::{callbacks, ClassPlugin, ErasedRegisterFn, PluginComponent};
5454
pub use crate::storage::as_storage;
5555
pub use crate::{
56-
gdext_register_method, gdext_register_method_inner, gdext_virtual_method_callback,
56+
gdext_get_arguments_info, gdext_register_method, gdext_register_method_inner,
57+
gdext_virtual_method_callback,
5758
};
5859

5960
use crate::{log, sys};

godot-core/src/macros.rs

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -229,20 +229,7 @@ macro_rules! gdext_register_method_inner {
229229

230230
// Arguments meta-information
231231
let argument_count = NUM_ARGS as u32;
232-
let mut arguments_info: [PropertyInfo; NUM_ARGS] = {
233-
let mut i = -1i32;
234-
[$(
235-
{
236-
i += 1;
237-
let prop = Sig::property_info(i, stringify!($param));
238-
//OnceArg::new(prop)
239-
prop
240-
},
241-
)*]
242-
};
243-
let mut arguments_info_sys: [sys::GDExtensionPropertyInfo; NUM_ARGS]
244-
= std::array::from_fn(|i| arguments_info[i].property_sys());
245-
// = std::array::from_fn(|i| arguments_info[i].once_sys());
232+
let mut arguments_info: [sys::GDExtensionPropertyInfo; NUM_ARGS] = $crate::gdext_get_arguments_info!(($($RetTy)+, $($ParamTy),*), $( $param, )*);
246233
let mut arguments_metadata: [sys::GDExtensionClassMethodArgumentMetadata; NUM_ARGS]
247234
= std::array::from_fn(|i| Sig::param_metadata(i as i32));
248235

@@ -262,7 +249,7 @@ macro_rules! gdext_register_method_inner {
262249
return_value_info: std::ptr::addr_of_mut!(return_value_info_sys),
263250
return_value_metadata,
264251
argument_count,
265-
arguments_info: arguments_info_sys.as_mut_ptr(),
252+
arguments_info: arguments_info.as_mut_ptr(),
266253
arguments_metadata: arguments_metadata.as_mut_ptr(),
267254
default_argument_count: 0,
268255
default_arguments: std::ptr::null_mut(),
@@ -621,3 +608,25 @@ macro_rules! gdext_ptrcall {
621608
);
622609
};
623610
}
611+
612+
#[doc(hidden)]
613+
#[macro_export]
614+
macro_rules! gdext_get_arguments_info {
615+
(
616+
$Signature:ty,
617+
$($param:ident,)*
618+
) => {
619+
{
620+
use $crate::builtin::meta::*;
621+
622+
let mut i = -1i32;
623+
[$(
624+
{
625+
i += 1;
626+
let prop = <$Signature as VarcallSignatureTuple>::property_info(i, stringify!($param)).property_sys();
627+
prop
628+
},
629+
)*]
630+
}
631+
};
632+
}

godot-macros/src/godot_api.rs

Lines changed: 40 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use crate::util;
88
use crate::util::bail;
99
use proc_macro2::{Ident, TokenStream};
1010
use quote::quote;
11-
use venial::{AttributeValue, Declaration, Error, Function, Impl, ImplMember};
11+
use venial::{AttributeValue, Declaration, Error, FnParam, Function, Impl, ImplMember, TyExpr};
1212

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

65-
//let register_fn = format_ident!("__godot_rust_register_{}", class_name_str);
66-
//#[allow(non_snake_case)]
67-
6865
let (funcs, signals) = process_godot_fns(&mut decl)?;
69-
let signal_name_strs = signals.into_iter().map(|ident| ident.to_string());
66+
67+
let mut signal_name_strs: Vec<String> = Vec::new();
68+
let mut signal_parameters_count: Vec<i64> = Vec::new();
69+
let mut signal_parameters: Vec<TokenStream> = Vec::new();
70+
71+
for signature in signals {
72+
let mut param_types: Vec<TyExpr> = Vec::new();
73+
let mut param_names: Vec<Ident> = Vec::new();
74+
75+
for param in signature.params.inner {
76+
match &param.0 {
77+
FnParam::Typed(param) => {
78+
param_types.push(param.ty.clone());
79+
param_names.push(param.name.clone());
80+
}
81+
FnParam::Receiver(_) => {}
82+
};
83+
}
84+
85+
signal_name_strs.push(signature.name.to_string());
86+
signal_parameters_count.push(param_names.len() as i64);
87+
signal_parameters.push(
88+
quote! {
89+
::godot::private::gdext_get_arguments_info!(((), #(#param_types ),*), #(#param_names, )*).as_ptr()
90+
},
91+
);
92+
}
7093

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

7396
let result = quote! {
7497
#decl
7598

7699
impl ::godot::obj::cap::ImplementsGodotApi for #class_name {
77-
//fn __register_methods(_builder: &mut ::godot::builder::ClassBuilder<Self>) {
78100
fn __register_methods() {
79101
#(
80102
::godot::private::gdext_register_method!(#class_name, #funcs);
@@ -83,14 +105,17 @@ fn transform_inherent_impl(mut decl: Impl) -> Result<TokenStream, Error> {
83105
unsafe {
84106
let class_name = ::godot::builtin::StringName::from(#class_name_str);
85107
use ::godot::sys;
108+
86109
#(
110+
let parameters = #signal_parameters;
87111
let signal_name = ::godot::builtin::StringName::from(#signal_name_strs);
112+
88113
sys::interface_fn!(classdb_register_extension_class_signal)(
89114
sys::get_library(),
90115
class_name.string_sys(),
91116
signal_name.string_sys(),
92-
std::ptr::null(), // NULL only valid for zero parameters, in current impl; maybe better empty slice
93-
0,
117+
parameters,
118+
sys::GDExtensionInt::from(#signal_parameters_count),
94119
);
95120
)*
96121
}
@@ -110,9 +135,9 @@ fn transform_inherent_impl(mut decl: Impl) -> Result<TokenStream, Error> {
110135
Ok(result)
111136
}
112137

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

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

154-
signal_idents.push(method.name.clone());
180+
signal_signatures.push(sig.clone());
155181
removed_indexes.push(index);
156182
}
157183
}
@@ -164,7 +190,7 @@ fn process_godot_fns(decl: &mut Impl) -> Result<(Vec<Function>, Vec<Ident>), Err
164190
decl.body_items.remove(index);
165191
}
166192

167-
Ok((func_signatures, signal_idents))
193+
Ok((func_signatures, signal_signatures))
168194
}
169195

170196
fn extract_attributes(method: &Function) -> Result<Option<BoundAttr>, Error> {

itest/rust/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ mod projection_test;
3131
mod quaternion_test;
3232
mod rect2i_test;
3333
mod rid_test;
34+
mod signal_test;
3435
mod singleton_test;
3536
mod string;
3637
mod transform2d_test;

itest/rust/src/signal_test.rs

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
/*
2+
* This Source Code Form is subject to the terms of the Mozilla Public
3+
* License, v. 2.0. If a copy of the MPL was not distributed with this
4+
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
5+
*/
6+
7+
use std::cell::Cell;
8+
9+
use godot::bind::{godot_api, GodotClass};
10+
use godot::builtin::{GodotString, Variant};
11+
12+
use godot::engine::Object;
13+
use godot::obj::{Base, Gd, Share};
14+
use godot::sys;
15+
16+
use crate::itest;
17+
18+
#[derive(GodotClass)]
19+
#[class(init, base=Object)]
20+
struct Emitter {
21+
#[base]
22+
base: Base<Object>,
23+
}
24+
25+
#[godot_api]
26+
impl Emitter {
27+
#[signal]
28+
fn signal_0_arg();
29+
#[signal]
30+
fn signal_1_arg(arg1: i64);
31+
#[signal]
32+
fn signal_2_arg(arg1: Gd<Object>, arg2: GodotString);
33+
}
34+
35+
#[derive(GodotClass)]
36+
#[class(init, base=Object)]
37+
struct Receiver {
38+
used: [Cell<bool>; 3],
39+
#[base]
40+
base: Base<Object>,
41+
}
42+
43+
#[godot_api]
44+
impl Receiver {
45+
#[func]
46+
fn receive_0_arg(&self) {
47+
self.used[0].set(true);
48+
}
49+
#[func]
50+
fn receive_1_arg(&self, arg1: i64) {
51+
self.used[1].set(true);
52+
assert_eq!(arg1, 987);
53+
}
54+
#[func]
55+
fn receive_2_arg(&self, arg1: Gd<Object>, arg2: GodotString) {
56+
assert_eq!(self.base.share(), arg1);
57+
assert_eq!(SIGNAL_ARG_STRING, arg2.to_string());
58+
59+
self.used[2].set(true);
60+
}
61+
}
62+
63+
const SIGNAL_ARG_STRING: &str = "Signal string arg";
64+
65+
#[itest]
66+
/// Test that godot can call a method that is connect with a signal
67+
fn signals() {
68+
let mut emitter = Gd::<Emitter>::new_default();
69+
let receiver = Gd::<Receiver>::new_default();
70+
71+
let args = [
72+
vec![],
73+
vec![Variant::from(987)],
74+
vec![
75+
Variant::from(receiver.share()),
76+
Variant::from(SIGNAL_ARG_STRING),
77+
],
78+
];
79+
80+
for (i, arg) in args.iter().enumerate() {
81+
let signal_name = format!("signal_{i}_arg");
82+
let receiver_name = format!("receive_{i}_arg");
83+
84+
emitter.bind_mut().connect(
85+
signal_name.clone().into(),
86+
receiver.callable(receiver_name),
87+
0,
88+
);
89+
90+
emitter.bind_mut().emit_signal(signal_name.into(), arg);
91+
92+
assert!(receiver.bind().used[i].get());
93+
}
94+
95+
receiver.free();
96+
emitter.free();
97+
}

0 commit comments

Comments
 (0)