gio/subclass/
dbus_proxy.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3#![deny(unsafe_op_in_unsafe_fn)]
4
5use glib::{prelude::*, subclass::prelude::*, translate::*};
6
7use crate::{
8    DBusProxy, ffi,
9    subclass::prelude::{AsyncInitableImpl, DBusInterfaceImpl, InitableImpl},
10};
11
12pub trait DBusProxyImpl:
13    ObjectImpl
14    + AsyncInitableImpl
15    + DBusInterfaceImpl
16    + InitableImpl
17    + ObjectSubclass<Type: IsA<DBusProxy>>
18{
19    /// Signal class handler for the #GDBusProxy::g-properties-changed signal.
20    fn g_properties_changed(
21        &self,
22        changed_properties: &glib::Variant,
23        invalidated_properties: &glib::StrVRef,
24    ) {
25        self.parent_g_properties_changed(changed_properties, invalidated_properties);
26    }
27
28    /// Signal class handler for the #GDBusProxy::g-signal signal.
29    fn g_signal(
30        &self,
31        sender_name: Option<&glib::GStr>,
32        signal_name: &glib::GStr,
33        parameters: &glib::Variant,
34    ) {
35        self.parent_g_signal(sender_name, signal_name, parameters);
36    }
37}
38
39pub trait DBusProxyImplExt: DBusProxyImpl {
40    fn parent_g_properties_changed(
41        &self,
42        changed_properties: &glib::Variant,
43        invalidated_properties: &glib::StrVRef,
44    ) {
45        unsafe {
46            let data = Self::type_data();
47            let parent_class = data.as_ref().parent_class() as *const ffi::GDBusProxyClass;
48
49            if let Some(f) = (*parent_class).g_properties_changed {
50                f(
51                    self.obj().unsafe_cast_ref::<DBusProxy>().to_glib_none().0,
52                    changed_properties.to_glib_none().0,
53                    invalidated_properties.to_glib_none().0,
54                );
55            }
56        }
57    }
58
59    fn parent_g_signal(
60        &self,
61        sender_name: Option<&glib::GStr>,
62        signal_name: &glib::GStr,
63        parameters: &glib::Variant,
64    ) {
65        unsafe {
66            let data = Self::type_data();
67            let parent_class = data.as_ref().parent_class() as *const ffi::GDBusProxyClass;
68
69            if let Some(f) = (*parent_class).g_signal {
70                f(
71                    self.obj().unsafe_cast_ref::<DBusProxy>().to_glib_none().0,
72                    sender_name.to_glib_none().0,
73                    signal_name.to_glib_none().0,
74                    parameters.to_glib_none().0,
75                );
76            }
77        }
78    }
79}
80
81impl<T: DBusProxyImpl> DBusProxyImplExt for T {}
82
83unsafe impl<T: DBusProxyImpl> IsSubclassable<T> for DBusProxy {
84    fn class_init(class: &mut glib::Class<Self>) {
85        Self::parent_class_init::<T>(class);
86        let class = class.as_mut();
87        class.g_properties_changed = Some(g_properties_changed::<T>);
88        class.g_signal = Some(g_signal::<T>);
89    }
90}
91
92unsafe extern "C" fn g_properties_changed<T: DBusProxyImpl>(
93    proxy: *mut ffi::GDBusProxy,
94    changed_properties: *mut glib::ffi::GVariant,
95    invalidated_properties: *const *const libc::c_char,
96) {
97    let instance = unsafe { &*(proxy as *mut T::Instance) };
98    let imp = instance.imp();
99
100    let changed_properties = unsafe { from_glib_borrow(changed_properties) };
101    let invalidated_properties = unsafe { glib::StrVRef::from_glib_borrow(invalidated_properties) };
102    imp.g_properties_changed(&changed_properties, invalidated_properties);
103}
104
105unsafe extern "C" fn g_signal<T: DBusProxyImpl>(
106    proxy: *mut ffi::GDBusProxy,
107    sender_name: *const libc::c_char,
108    signal_name: *const libc::c_char,
109    parameters: *mut glib::ffi::GVariant,
110) {
111    let instance = unsafe { &*(proxy as *mut T::Instance) };
112    let imp = instance.imp();
113
114    let sender_name = unsafe { Option::<&glib::GStr>::from_glib_none(sender_name) };
115    let signal_name = unsafe { from_glib_none(signal_name) };
116    let parameters = unsafe { from_glib_borrow(parameters) };
117    imp.g_signal(sender_name, signal_name, &parameters);
118}
119
120#[cfg(test)]
121mod tests {
122    use super::*;
123
124    use std::cell::RefCell;
125
126    use crate::{
127        AsyncInitable, Cancellable, DBusConnection, DBusConnectionFlags, DBusInterface, Initable,
128        MemoryInputStream, MemoryOutputStream, SimpleIOStream,
129    };
130
131    mod imp {
132        use super::*;
133
134        #[derive(Default)]
135        pub struct CustomDBusProxyImpl {
136            pub(super) g_properties_changed_called: RefCell<bool>,
137            pub(super) g_signal_called: RefCell<bool>,
138        }
139
140        #[glib::object_subclass]
141        impl ObjectSubclass for CustomDBusProxyImpl {
142            const NAME: &'static str = "CustomDBusProxyImpl";
143            type Type = super::CustomDBusProxyImpl;
144            type ParentType = DBusProxy;
145            type Interfaces = (DBusInterface, Initable, AsyncInitable);
146        }
147
148        impl ObjectImpl for CustomDBusProxyImpl {}
149
150        impl InitableImpl for CustomDBusProxyImpl {}
151        impl AsyncInitableImpl for CustomDBusProxyImpl {}
152        impl DBusInterfaceImpl for CustomDBusProxyImpl {}
153
154        impl DBusProxyImpl for CustomDBusProxyImpl {
155            fn g_signal(
156                &self,
157                sender_name: Option<&glib::GStr>,
158                signal_name: &glib::GStr,
159                parameters: &glib::Variant,
160            ) {
161                *self.g_signal_called.borrow_mut() = true;
162                self.parent_g_signal(sender_name, signal_name, parameters);
163            }
164
165            fn g_properties_changed(
166                &self,
167                changed_properties: &glib::Variant,
168                invalidated_properties: &glib::StrVRef,
169            ) {
170                *self.g_properties_changed_called.borrow_mut() = true;
171                self.parent_g_properties_changed(changed_properties, invalidated_properties);
172            }
173        }
174    }
175
176    glib::wrapper! {
177        pub struct CustomDBusProxyImpl(ObjectSubclass<imp::CustomDBusProxyImpl>)
178            @extends DBusProxy,
179            @implements DBusInterface, Initable, AsyncInitable;
180    }
181
182    #[test]
183    fn g_signal_is_called() {
184        let proxy = create_custom_proxy_impl();
185        let sender_name = "org.example.Sender";
186        let signal_name = "example";
187        let parameters = glib::Variant::array_from_iter::<glib::Variant>([]);
188        proxy
189            .upcast_ref::<DBusProxy>()
190            .emit_by_name::<()>("g-signal", &[&sender_name, &signal_name, &parameters]);
191        assert!(*proxy.imp().g_signal_called.borrow());
192    }
193
194    #[test]
195    fn g_properties_changed_is_called() {
196        let proxy = create_custom_proxy_impl();
197        let changed_properties = glib::Variant::array_from_iter::<String>([]);
198        let invalidated_properties = glib::StrV::new();
199        proxy.upcast_ref::<DBusProxy>().emit_by_name::<()>(
200            "g-properties-changed",
201            &[&changed_properties, &invalidated_properties],
202        );
203        assert!(*proxy.imp().g_properties_changed_called.borrow());
204    }
205
206    fn create_custom_proxy_impl() -> CustomDBusProxyImpl {
207        // By providing a connection, we prevent the proxy
208        // from trying to establish a real DBus connection.
209        let connection = create_no_op_dbus_connection();
210        Initable::builder()
211            .property("g-connection", connection)
212            .property("g-object-path", "/org/example/test")
213            .property("g-interface-name", "org.example.Test")
214            .build(Option::<&Cancellable>::None)
215            .expect("failed to create CustomDBusProxyImpl")
216    }
217
218    fn create_no_op_dbus_connection() -> DBusConnection {
219        let input = MemoryInputStream::new();
220        let output = MemoryOutputStream::new_resizable();
221        let stream = SimpleIOStream::new(&input, &output);
222        DBusConnection::new_sync(
223            &stream,
224            None,
225            DBusConnectionFlags::NONE,
226            None,
227            Option::<&Cancellable>::None,
228        )
229        .expect("failed to create DBusConnection")
230    }
231}