Skip to content

Commit 11797f8

Browse files
authored
Merge pull request #570 from TitanNano/jovan/signal
Implement builtin type `Signal`
2 parents 48e1d65 + f90ba67 commit 11797f8

File tree

8 files changed

+282
-60
lines changed

8 files changed

+282
-60
lines changed

godot-core/src/builtin/macros.rs

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -123,34 +123,6 @@ macro_rules! impl_builtin_traits {
123123
)
124124
}
125125

126-
macro_rules! impl_builtin_stub {
127-
// ($Class:ident, $OpaqueTy:ident $( ; )? $( $Traits:ident ),* ) => {
128-
($Class:ident, $OpaqueTy:ident) => {
129-
#[repr(C)]
130-
// #[derive(Copy, Clone)]
131-
pub struct $Class {
132-
opaque: sys::types::$OpaqueTy,
133-
}
134-
135-
impl $Class {
136-
fn from_opaque(opaque: sys::types::$OpaqueTy) -> Self {
137-
Self { opaque }
138-
}
139-
}
140-
141-
// SAFETY:
142-
// This is simply a wrapper around an `Opaque` value representing a value of the type.
143-
// So this is safe.
144-
unsafe impl GodotFfi for $Class {
145-
fn variant_type() -> sys::VariantType {
146-
sys::VariantType::$Class
147-
}
148-
149-
ffi_methods! { type sys::GDExtensionTypePtr = *mut Opaque; .. }
150-
}
151-
};
152-
}
153-
154126
macro_rules! impl_builtin_froms {
155127
($To:ty; $($From:ty => $from_fn:ident),* $(,)?) => {
156128
$(impl From<&$From> for $To {

godot-core/src/builtin/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@ pub use basis::*;
4242
pub use callable::*;
4343
pub use color::*;
4444
pub use dictionary_inner::Dictionary;
45-
pub use others::*;
4645
pub use packed_array::*;
4746
pub use plane::*;
4847
pub use projection::*;
@@ -51,6 +50,7 @@ pub use real_inner::*;
5150
pub use rect2::*;
5251
pub use rect2i::*;
5352
pub use rid::*;
53+
pub use signal::*;
5454
pub use string::*;
5555
pub use transform2d::*;
5656
pub use transform3d::*;
@@ -84,14 +84,14 @@ mod aabb;
8484
mod basis;
8585
mod callable;
8686
mod color;
87-
mod others;
8887
mod packed_array;
8988
mod plane;
9089
mod projection;
9190
mod quaternion;
9291
mod rect2;
9392
mod rect2i;
9493
mod rid;
94+
mod signal;
9595
mod string;
9696
mod transform2d;
9797
mod transform3d;

godot-core/src/builtin/others.rs

Lines changed: 0 additions & 25 deletions
This file was deleted.

godot-core/src/builtin/signal.rs

Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
/*
2+
* Copyright (c) godot-rust; Bromeon and contributors.
3+
* This Source Code Form is subject to the terms of the Mozilla Public
4+
* License, v. 2.0. If a copy of the MPL was not distributed with this
5+
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
6+
*/
7+
8+
use std::fmt;
9+
use std::ptr;
10+
11+
use godot_ffi as sys;
12+
13+
use super::meta::{impl_godot_as_self, FromGodot, GodotType};
14+
use crate::builtin::meta::ToGodot;
15+
use crate::builtin::{inner, Array, Callable, Dictionary, StringName, Variant};
16+
use crate::engine::{global::Error, Object};
17+
use crate::obj::bounds::DynMemory;
18+
use crate::obj::{Bounds, Gd, GodotClass, InstanceId};
19+
use sys::{ffi_methods, GodotFfi};
20+
21+
/// A `Signal` represents a signal of an Object instance in Godot.
22+
///
23+
/// Signals are composed of a reference to an `Object` and the name of the signal on this object.
24+
#[repr(C, align(8))]
25+
pub struct Signal {
26+
opaque: sys::types::OpaqueSignal,
27+
}
28+
29+
impl Signal {
30+
fn from_opaque(opaque: sys::types::OpaqueSignal) -> Self {
31+
Self { opaque }
32+
}
33+
34+
/// Create a signal for the signal `object::signal_name`.
35+
///
36+
/// _Godot equivalent: `Signal(Object object, StringName signal)`_
37+
pub fn from_object_signal<T, S>(object: &Gd<T>, signal_name: S) -> Self
38+
where
39+
T: GodotClass,
40+
S: Into<StringName>,
41+
{
42+
let signal_name = signal_name.into();
43+
unsafe {
44+
sys::from_sys_init_or_init_default::<Self>(|self_ptr| {
45+
let ctor = sys::builtin_fn!(signal_from_object_signal);
46+
let raw = object.to_ffi();
47+
let args = [raw.as_arg_ptr(), signal_name.sys_const()];
48+
ctor(self_ptr, args.as_ptr());
49+
})
50+
}
51+
}
52+
53+
/// Creates an invalid/empty signal that is not able to be called.
54+
///
55+
/// _Godot equivalent: `Signal()`_
56+
pub fn invalid() -> Self {
57+
unsafe {
58+
Self::from_sys_init(|self_ptr| {
59+
let ctor = sys::builtin_fn!(signal_construct_default);
60+
ctor(self_ptr, ptr::null_mut())
61+
})
62+
}
63+
}
64+
65+
/// Connects this signal to the specified callable.
66+
///
67+
/// Optional flags can be also added to configure the connection's behavior (see [`ConnectFlags`](crate::engine::object::ConnectFlags) constants).
68+
/// You can provide additional arguments to the connected callable by using `Callable::bind`.
69+
///
70+
/// A signal can only be connected once to the same [`Callable`]. If the signal is already connected,
71+
/// returns [`Error::ERR_INVALID_PARAMETER`] and
72+
/// pushes an error message, unless the signal is connected with [`ConnectFlags::REFERENCE_COUNTED`](crate::engine::object::ConnectFlags::REFERENCE_COUNTED).
73+
/// To prevent this, use [`Self::is_connected`] first to check for existing connections.
74+
pub fn connect(&self, callable: Callable, flags: i64) -> Error {
75+
let error = self.as_inner().connect(callable, flags);
76+
77+
Error::from_godot(error as i32)
78+
}
79+
80+
/// Disconnects this signal from the specified [`Callable`].
81+
///
82+
/// If the connection does not exist, generates an error. Use [`Self::is_connected`] to make sure that the connection exists.
83+
pub fn disconnect(&self, callable: Callable) {
84+
self.as_inner().disconnect(callable);
85+
}
86+
87+
/// Emits this signal.
88+
///
89+
/// All Callables connected to this signal will be triggered.
90+
pub fn emit(&self, varargs: &[Variant]) {
91+
let Some(mut object) = self.object() else {
92+
return;
93+
};
94+
95+
object.emit_signal(self.name(), varargs);
96+
}
97+
98+
/// Returns an [`Array`] of connections for this signal.
99+
///
100+
/// Each connection is represented as a Dictionary that contains three entries:
101+
/// - `signal` is a reference to this [`Signal`];
102+
/// - `callable` is a reference to the connected [`Callable`];
103+
/// - `flags` is a combination of [`ConnectFlags`](crate::engine::object::ConnectFlags).
104+
///
105+
/// _Godot equivalent: `get_connections`_
106+
pub fn connections(&self) -> Array<Dictionary> {
107+
self.as_inner()
108+
.get_connections()
109+
.iter_shared()
110+
.map(|variant| variant.to())
111+
.collect()
112+
}
113+
114+
/// Returns the name of the signal.
115+
pub fn name(&self) -> StringName {
116+
self.as_inner().get_name()
117+
}
118+
119+
/// Returns the object to which this signal belongs.
120+
///
121+
/// Returns [`None`] when this signal doesn't have any object.
122+
///
123+
/// _Godot equivalent: `get_object`_
124+
pub fn object(&self) -> Option<Gd<Object>> {
125+
self.as_inner().get_object().map(|mut object| {
126+
<Object as Bounds>::DynMemory::maybe_inc_ref(&mut object.raw);
127+
object
128+
})
129+
}
130+
131+
/// Returns the ID of this signal's object, see also [`Gd::instance_id`].
132+
///
133+
/// Returns [`None`] when this signal doesn't have any object.
134+
///
135+
/// _Godot equivalent: `get_object_id`_
136+
pub fn object_id(&self) -> Option<InstanceId> {
137+
let id = self.as_inner().get_object_id();
138+
InstanceId::try_from_i64(id)
139+
}
140+
141+
/// Returns `true` if the specified [`Callable`] is connected to this signal.
142+
pub fn is_connected(&self, callable: Callable) -> bool {
143+
self.as_inner().is_connected(callable)
144+
}
145+
146+
/// Returns `true` if the signal's name does not exist in its object, or the object is not valid.
147+
pub fn is_null(&self) -> bool {
148+
self.as_inner().is_null()
149+
}
150+
151+
#[doc(hidden)]
152+
pub fn as_inner(&self) -> inner::InnerSignal {
153+
inner::InnerSignal::from_outer(self)
154+
}
155+
}
156+
157+
// SAFETY:
158+
// The `opaque` in `Signal` is just a pair of pointers, and requires no special initialization or cleanup
159+
// beyond what is done in `from_opaque` and `drop`. So using `*mut Opaque` is safe.
160+
unsafe impl GodotFfi for Signal {
161+
fn variant_type() -> sys::VariantType {
162+
sys::VariantType::Signal
163+
}
164+
165+
ffi_methods! { type sys::GDExtensionTypePtr = *mut Opaque;
166+
fn from_sys;
167+
fn sys;
168+
fn from_sys_init;
169+
fn move_return_ptr;
170+
}
171+
172+
unsafe fn from_arg_ptr(ptr: sys::GDExtensionTypePtr, _call_type: sys::PtrcallType) -> Self {
173+
Self::from_sys(ptr)
174+
}
175+
176+
#[cfg(before_api = "4.1")]
177+
unsafe fn from_sys_init_default(init_fn: impl FnOnce(sys::GDExtensionTypePtr)) -> Self {
178+
let mut result = Self::invalid();
179+
init_fn(result.sys_mut());
180+
result
181+
}
182+
}
183+
184+
impl_builtin_traits! {
185+
for Signal {
186+
Clone => signal_construct_copy;
187+
Drop => signal_destroy;
188+
PartialEq => signal_operator_equal;
189+
}
190+
}
191+
192+
impl_godot_as_self!(Signal);
193+
194+
impl fmt::Debug for Signal {
195+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
196+
let name = self.name();
197+
let object = self.object();
198+
199+
f.debug_struct("signal")
200+
.field("name", &name)
201+
.field("object", &object)
202+
.finish()
203+
}
204+
}
205+
206+
impl fmt::Display for Signal {
207+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
208+
write!(f, "{}", self.to_variant())
209+
}
210+
}

godot-core/src/property.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -431,9 +431,10 @@ mod export_impls {
431431
impl_property_by_clone!(u16 => Int; int);
432432
impl_property_by_clone!(u8 => Int; int);
433433

434-
// Callables are useless when exported to the editor, so we only need to make them available as
434+
// Callables and Signals are useless when exported to the editor, so we only need to make them available as
435435
// properties.
436436
impl_property_by_clone!(Callable => Callable, no_export);
437+
impl_property_by_clone!(Signal => Signal, no_export);
437438

438439
// RIDs when exported act slightly weird. They are largely read-only, however you can reset them to their
439440
// default value. This seems to me very unintuitive. Since if we are storing an RID we would likely not

itest/rust/build.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ fn collect_inputs() -> Vec<Input> {
127127
push!(inputs; Vector3i, Vector3i, Vector3i(-1, -2147483648, 2147483647), Vector3i::new(-1, -2147483648, 2147483647));
128128
push!(inputs; Vector4i, Vector4i, Vector4i(-1, -2147483648, 2147483647, 1000), Vector4i::new(-1, -2147483648, 2147483647, 100));
129129
pushs!(inputs; Callable, Callable, "Callable()", Callable::invalid(), true, false, Some(quote! { Callable::invalid() }));
130+
pushs!(inputs; Signal, Signal, "Signal()", Signal::invalid(), true, false, Some(quote! { Signal::invalid() }));
130131
push!(inputs; Rect2, Rect2, Rect2(), Rect2::default());
131132
push!(inputs; Rect2i, Rect2i, Rect2i(), Rect2i::default());
132133
push!(inputs; Transform2D, Transform2D, Transform2D(), Transform2D::default());

0 commit comments

Comments
 (0)