/rust/registry/src/index.crates.io-1949cf8c6b5b557f/dbus-0.9.7/src/blocking.rs
Line | Count | Source |
1 | | //! Connections and proxies that make blocking method calls. |
2 | | |
3 | | |
4 | | use crate::strings::{BusName, Path, Interface, Member}; |
5 | | use crate::arg::{AppendAll, ReadAll, IterAppend}; |
6 | | use crate::{channel, Error, Message}; |
7 | | use crate::message::{MatchRule, SignalArgs, MessageType}; |
8 | | use crate::channel::{Channel, BusType, Token}; |
9 | | use std::{cell::RefCell, time::Duration, sync::Mutex}; |
10 | | use std::sync::atomic::{AtomicBool, Ordering}; |
11 | | use crate::filters::Filters; |
12 | | |
13 | | #[allow(missing_docs)] |
14 | | mod generated_org_freedesktop_standard_interfaces; |
15 | | mod generated_org_freedesktop_dbus; |
16 | | |
17 | | /// This module contains some standard interfaces and an easy way to call them. |
18 | | /// |
19 | | /// See the [D-Bus specification](https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces) for more information about these standard interfaces. |
20 | | /// |
21 | | /// The code was created by dbus-codegen. |
22 | | pub mod stdintf { |
23 | | #[allow(missing_docs)] |
24 | | pub mod org_freedesktop_dbus { |
25 | | pub use super::super::generated_org_freedesktop_standard_interfaces::*; |
26 | | |
27 | | #[derive(Debug, PartialEq, Eq, Copy, Clone)] |
28 | | pub enum RequestNameReply { |
29 | | PrimaryOwner = 1, |
30 | | InQueue = 2, |
31 | | Exists = 3, |
32 | | AlreadyOwner = 4, |
33 | | } |
34 | | |
35 | | #[derive(Debug, PartialEq, Eq, Copy, Clone)] |
36 | | pub enum ReleaseNameReply { |
37 | | Released = 1, |
38 | | NonExistent = 2, |
39 | | NotOwner = 3, |
40 | | } |
41 | | |
42 | | #[derive(Debug, PartialEq, Eq, Copy, Clone)] |
43 | | pub enum EmitsChangedSignal { |
44 | | True, |
45 | | Invalidates, |
46 | | Const, |
47 | | False, |
48 | | } |
49 | | |
50 | 0 | pub (crate) fn request_name<S: crate::blocking::BlockingSender>(s: &S, name: &str, allow_replacement: bool, replace_existing: bool, do_not_queue: bool) |
51 | 0 | -> Result<RequestNameReply, crate::Error> { |
52 | 0 | let flags: u32 = |
53 | 0 | if allow_replacement { 1 } else { 0 } + |
54 | 0 | if replace_existing { 2 } else { 0 } + |
55 | 0 | if do_not_queue { 4 } else { 0 }; |
56 | 0 | let proxy = super::proxy(s); |
57 | | use super::org_freedesktop::DBus; |
58 | 0 | let r = proxy.request_name(name, flags)?; |
59 | | use RequestNameReply::*; |
60 | 0 | let all = [PrimaryOwner, InQueue, Exists, AlreadyOwner]; |
61 | 0 | all.iter().find(|x| **x as u32 == r).copied().ok_or_else(|| |
62 | 0 | crate::Error::new_failed("Invalid reply from DBus server") |
63 | | ) |
64 | 0 | } |
65 | | |
66 | 0 | pub (crate) fn release_name<S: crate::blocking::BlockingSender>(s: &S, name: &str) |
67 | 0 | -> Result<ReleaseNameReply, crate::Error> { |
68 | | |
69 | 0 | let proxy = super::proxy(s); |
70 | | use super::org_freedesktop::DBus; |
71 | 0 | let r = proxy.release_name(name)?; |
72 | | use ReleaseNameReply::*; |
73 | 0 | let all = [Released, NonExistent, NotOwner]; |
74 | 0 | all.iter().find(|x| **x as u32 == r).copied().ok_or_else(|| |
75 | 0 | crate::Error::new_failed("Invalid reply from DBus server") |
76 | | ) |
77 | 0 | } |
78 | | |
79 | | use crate::arg; |
80 | | impl PropertiesPropertiesChanged { |
81 | 0 | pub fn add_prop<F: FnOnce() -> Box<dyn arg::RefArg>>(&mut self, prop_name: &str, emits: EmitsChangedSignal, f: F) -> bool { |
82 | 0 | match emits { |
83 | 0 | EmitsChangedSignal::False => { false }, |
84 | | EmitsChangedSignal::Invalidates => { |
85 | 0 | if !self.invalidated_properties.iter().any(|x| x == prop_name) { |
86 | 0 | self.invalidated_properties.push(prop_name.into()) |
87 | 0 | } |
88 | 0 | true |
89 | | } |
90 | | EmitsChangedSignal::True => { |
91 | 0 | let val = f(); |
92 | 0 | self.changed_properties.insert(prop_name.into(), arg::Variant(val)); |
93 | 0 | true |
94 | | } |
95 | 0 | EmitsChangedSignal::Const => panic!("Called add_prop with EmitsChangedSignal::Const") |
96 | | } |
97 | 0 | } |
98 | | } |
99 | | } |
100 | | |
101 | | // Not public yet, because of lack of named arguments |
102 | | pub (super) mod org_freedesktop { |
103 | | pub(crate) use super::super::generated_org_freedesktop_dbus::*; |
104 | | } |
105 | | |
106 | 0 | pub (crate) fn proxy<C>(c: C) -> crate::blocking::Proxy<'static, C> { |
107 | 0 | super::Proxy::new("org.freedesktop.DBus", "/org/freedesktop/DBus", std::time::Duration::from_millis(5000), c) |
108 | 0 | } Unexecuted instantiation: dbus::blocking::stdintf::proxy::<&dbus::blocking::Connection> Unexecuted instantiation: dbus::blocking::stdintf::proxy::<&dbus::blocking::SyncConnection> Unexecuted instantiation: dbus::blocking::stdintf::proxy::<&dbus::blocking::LocalConnection> |
109 | | } |
110 | | |
111 | | /// A connection to D-Bus, thread local + non-async version |
112 | | pub struct LocalConnection { |
113 | | channel: Channel, |
114 | | filters: RefCell<Filters<LocalFilterCb>>, |
115 | | all_signal_matches: AtomicBool, |
116 | | } |
117 | | |
118 | | /// A connection to D-Bus, non-async version where callbacks are Send but not Sync. |
119 | | pub struct Connection { |
120 | | channel: Channel, |
121 | | filters: RefCell<Filters<FilterCb>>, |
122 | | all_signal_matches: AtomicBool, |
123 | | } |
124 | | |
125 | | /// A connection to D-Bus, Send + Sync + non-async version |
126 | | pub struct SyncConnection { |
127 | | channel: Channel, |
128 | | filters: Mutex<Filters<SyncFilterCb>>, |
129 | | all_signal_matches: AtomicBool, |
130 | | } |
131 | | |
132 | | use crate::blocking::stdintf::org_freedesktop_dbus; |
133 | | |
134 | | macro_rules! connimpl { |
135 | | ($c: ident, $cb: ident $(, $ss:tt)*) => { |
136 | | |
137 | | type |
138 | | $cb = Box<dyn FnMut(Message, &$c) -> bool $(+ $ss)* + 'static>; |
139 | | |
140 | | |
141 | | impl $c { |
142 | | |
143 | | /// Create a new connection to the session bus. |
144 | 0 | pub fn new_session() -> Result<Self, Error> { |
145 | 0 | Channel::get_private(BusType::Session).map(From::from) |
146 | 0 | } Unexecuted instantiation: <dbus::blocking::Connection>::new_session Unexecuted instantiation: <dbus::blocking::LocalConnection>::new_session Unexecuted instantiation: <dbus::blocking::SyncConnection>::new_session |
147 | | |
148 | | /// Create a new connection to the system-wide bus. |
149 | 0 | pub fn new_system() -> Result<Self, Error> { |
150 | 0 | Channel::get_private(BusType::System).map(From::from) |
151 | 0 | } Unexecuted instantiation: <dbus::blocking::Connection>::new_system Unexecuted instantiation: <dbus::blocking::LocalConnection>::new_system Unexecuted instantiation: <dbus::blocking::SyncConnection>::new_system |
152 | | |
153 | | /// Get the connection's unique name. |
154 | | /// |
155 | | /// It's usually something like ":1.54" |
156 | 0 | pub fn unique_name(&self) -> BusName { self.channel.unique_name().unwrap().into() }Unexecuted instantiation: <dbus::blocking::Connection>::unique_name Unexecuted instantiation: <dbus::blocking::LocalConnection>::unique_name Unexecuted instantiation: <dbus::blocking::SyncConnection>::unique_name |
157 | | |
158 | | /// Create a convenience struct for easier calling of many methods on the same destination and path. |
159 | 0 | pub fn with_proxy<'a, 'b, D: Into<BusName<'a>>, P: Into<Path<'a>>>(&'b self, dest: D, path: P, timeout: Duration) -> |
160 | 0 | Proxy<'a, &'b Self> { |
161 | 0 | Proxy { connection: self, destination: dest.into(), path: path.into(), timeout } |
162 | 0 | } Unexecuted instantiation: <dbus::blocking::Connection>::with_proxy::<_, _> Unexecuted instantiation: <dbus::blocking::LocalConnection>::with_proxy::<_, _> Unexecuted instantiation: <dbus::blocking::SyncConnection>::with_proxy::<_, _> |
163 | | |
164 | | |
165 | | /// Request a name on the D-Bus. |
166 | | /// |
167 | | /// For detailed information on the flags and return values, see the libdbus documentation. |
168 | 0 | pub fn request_name<'a, N: Into<BusName<'a>>>(&self, name: N, allow_replacement: bool, replace_existing: bool, do_not_queue: bool) |
169 | 0 | -> Result<org_freedesktop_dbus::RequestNameReply, Error> { |
170 | 0 | org_freedesktop_dbus::request_name(&self.channel, &name.into(), allow_replacement, replace_existing, do_not_queue) |
171 | 0 | } Unexecuted instantiation: <dbus::blocking::Connection>::request_name::<_> Unexecuted instantiation: <dbus::blocking::LocalConnection>::request_name::<_> Unexecuted instantiation: <dbus::blocking::SyncConnection>::request_name::<_> |
172 | | |
173 | | /// Release a previously requested name on the D-Bus. |
174 | 0 | pub fn release_name<'a, N: Into<BusName<'a>>>(&self, name: N) -> Result<org_freedesktop_dbus::ReleaseNameReply, Error> { |
175 | 0 | org_freedesktop_dbus::release_name(&self.channel, &name.into()) |
176 | 0 | } Unexecuted instantiation: <dbus::blocking::Connection>::release_name::<_> Unexecuted instantiation: <dbus::blocking::LocalConnection>::release_name::<_> Unexecuted instantiation: <dbus::blocking::SyncConnection>::release_name::<_> |
177 | | |
178 | | /// Adds a new match to the connection, and sets up a callback when this message arrives. |
179 | | /// |
180 | | /// If multiple [`MatchRule`]s match the same message, then by default only the first match will |
181 | | /// get the callback. This behaviour can be changed for signal messages by calling |
182 | | /// [`set_signal_match_mode`](Self::set_signal_match_mode). |
183 | | /// |
184 | | /// The returned value can be used to remove the match. The match is also removed if the callback |
185 | | /// returns "false". |
186 | 0 | pub fn add_match<S: ReadAll, F>(&self, match_rule: MatchRule<'static>, f: F) -> Result<Token, Error> |
187 | 0 | where F: FnMut(S, &Self, &Message) -> bool $(+ $ss)* + 'static { |
188 | 0 | let m = match_rule.match_str(); |
189 | 0 | self.add_match_no_cb(&m)?; |
190 | | use channel::MatchingReceiver; |
191 | 0 | Ok(self.start_receive(match_rule, MakeSignal::make(f, m))) |
192 | 0 | } Unexecuted instantiation: <dbus::blocking::Connection>::add_match::<_, _> Unexecuted instantiation: <dbus::blocking::LocalConnection>::add_match::<_, _> Unexecuted instantiation: <dbus::blocking::SyncConnection>::add_match::<_, _> |
193 | | |
194 | | /// Adds a new match to the connection, without setting up a callback when this message arrives. |
195 | 0 | pub fn add_match_no_cb(&self, match_str: &str) -> Result<(), Error> { |
196 | | use crate::blocking::stdintf::org_freedesktop::DBus; |
197 | 0 | let proxy = stdintf::proxy(self); |
198 | 0 | proxy.add_match(match_str) |
199 | 0 | } Unexecuted instantiation: <dbus::blocking::Connection>::add_match_no_cb Unexecuted instantiation: <dbus::blocking::LocalConnection>::add_match_no_cb Unexecuted instantiation: <dbus::blocking::SyncConnection>::add_match_no_cb |
200 | | |
201 | | /// Removes a match from the connection, without removing any callbacks. |
202 | 0 | pub fn remove_match_no_cb(&self, match_str: &str) -> Result<(), Error> { |
203 | | use crate::blocking::stdintf::org_freedesktop::DBus; |
204 | 0 | let proxy = stdintf::proxy(self); |
205 | 0 | proxy.remove_match(match_str) |
206 | 0 | } Unexecuted instantiation: <dbus::blocking::Connection>::remove_match_no_cb Unexecuted instantiation: <dbus::blocking::LocalConnection>::remove_match_no_cb Unexecuted instantiation: <dbus::blocking::SyncConnection>::remove_match_no_cb |
207 | | |
208 | | /// Removes a previously added match and callback from the connection. |
209 | 0 | pub fn remove_match(&self, id: Token) -> Result<(), Error> { |
210 | | use channel::MatchingReceiver; |
211 | 0 | let (mr, _) = self.stop_receive(id).ok_or_else(|| Error::new_failed("No match with that id found"))?;Unexecuted instantiation: <dbus::blocking::Connection>::remove_match::{closure#0}Unexecuted instantiation: <dbus::blocking::LocalConnection>::remove_match::{closure#0}Unexecuted instantiation: <dbus::blocking::SyncConnection>::remove_match::{closure#0} |
212 | 0 | self.remove_match_no_cb(&mr.match_str()) |
213 | 0 | } Unexecuted instantiation: <dbus::blocking::Connection>::remove_match Unexecuted instantiation: <dbus::blocking::LocalConnection>::remove_match Unexecuted instantiation: <dbus::blocking::SyncConnection>::remove_match |
214 | | |
215 | | /// If true, configures the connection to send signal messages to all matching [`MatchRule`] |
216 | | /// filters added with [`add_match`](Self::add_match) rather than just the first one. This comes |
217 | | /// with the following gotchas: |
218 | | /// |
219 | | /// * The messages might be duplicated, so the message serial might be lost (this is |
220 | | /// generally not a problem for signals). |
221 | | /// * Panicking inside a match callback might mess with other callbacks, causing them |
222 | | /// to be permanently dropped. |
223 | | /// * Removing other matches from inside a match callback is not supported. |
224 | | /// |
225 | | /// This is false by default, for a newly-created connection. |
226 | 0 | pub fn set_signal_match_mode(&self, match_all: bool) { |
227 | 0 | self.all_signal_matches.store(match_all, Ordering::Release); |
228 | 0 | } Unexecuted instantiation: <dbus::blocking::Connection>::set_signal_match_mode Unexecuted instantiation: <dbus::blocking::LocalConnection>::set_signal_match_mode Unexecuted instantiation: <dbus::blocking::SyncConnection>::set_signal_match_mode |
229 | | |
230 | | /// Tries to handle an incoming message if there is one. If there isn't one, |
231 | | /// it will wait up to timeout |
232 | | /// |
233 | | /// This method only takes "&self" instead of "&mut self", but it is a logic error to call |
234 | | /// it recursively and might lead to panics or deadlocks. |
235 | | /// |
236 | | /// For `SyncConnection`: It is also a logic error to call this method from one thread, while |
237 | | /// calling this or other methods from other threads. This can lead to messages being lost. |
238 | 0 | pub fn process(&self, timeout: Duration) -> Result<bool, Error> { |
239 | 0 | if let Some(msg) = self.channel.blocking_pop_message(timeout)? { |
240 | 0 | if self.all_signal_matches.load(Ordering::Acquire) && msg.msg_type() == MessageType::Signal { |
241 | | // If it's a signal and the mode is enabled, send a copy of the message to all |
242 | | // matching filters. |
243 | 0 | let matching_filters = self.filters_mut().remove_all_matching(&msg); |
244 | | // `matching_filters` needs to be a separate variable and not inlined here, because |
245 | | // if it's inline then the `MutexGuard` will live too long and we'll get a deadlock |
246 | | // on the next call to `filters_mut()` below. |
247 | 0 | for mut ff in matching_filters { |
248 | 0 | if let Ok(copy) = msg.duplicate() { |
249 | 0 | if ff.2(copy, self) { |
250 | 0 | self.filters_mut().insert(ff); |
251 | 0 | } |
252 | 0 | } else { |
253 | 0 | // Silently drop the message, but add the filter back. |
254 | 0 | self.filters_mut().insert(ff); |
255 | 0 | } |
256 | | } |
257 | | } else { |
258 | | // Otherwise, send the original message to only the first matching filter. |
259 | 0 | let ff = self.filters_mut().remove_first_matching(&msg); |
260 | 0 | if let Some(mut ff) = ff { |
261 | 0 | if ff.2(msg, self) { |
262 | 0 | self.filters_mut().insert(ff); |
263 | 0 | } |
264 | 0 | } else if let Some(reply) = crate::channel::default_reply(&msg) { |
265 | 0 | let _ = self.channel.send(reply); |
266 | 0 | } |
267 | | } |
268 | 0 | Ok(true) |
269 | | } else { |
270 | 0 | Ok(false) |
271 | | } |
272 | 0 | } Unexecuted instantiation: <dbus::blocking::Connection>::process Unexecuted instantiation: <dbus::blocking::LocalConnection>::process Unexecuted instantiation: <dbus::blocking::SyncConnection>::process |
273 | | |
274 | | /// The channel for this connection |
275 | 0 | pub fn channel(&self) -> &Channel { |
276 | 0 | &self.channel |
277 | 0 | } Unexecuted instantiation: <dbus::blocking::Connection>::channel Unexecuted instantiation: <dbus::blocking::LocalConnection>::channel Unexecuted instantiation: <dbus::blocking::SyncConnection>::channel |
278 | | } |
279 | | |
280 | | impl BlockingSender for $c { |
281 | 0 | fn send_with_reply_and_block(&self, msg: Message, timeout: Duration) -> Result<Message, Error> { |
282 | 0 | self.channel.send_with_reply_and_block(msg, timeout) |
283 | 0 | } Unexecuted instantiation: <dbus::blocking::Connection as dbus::blocking::BlockingSender>::send_with_reply_and_block Unexecuted instantiation: <dbus::blocking::LocalConnection as dbus::blocking::BlockingSender>::send_with_reply_and_block Unexecuted instantiation: <dbus::blocking::SyncConnection as dbus::blocking::BlockingSender>::send_with_reply_and_block |
284 | | } |
285 | | |
286 | | impl From<Channel> for $c { |
287 | 0 | fn from(channel: Channel) -> $c { $c { |
288 | 0 | channel, |
289 | 0 | filters: Default::default(), |
290 | 0 | all_signal_matches: AtomicBool::new(false), |
291 | 0 | } } Unexecuted instantiation: <dbus::blocking::Connection as core::convert::From<dbus::channel::ffichannel::Channel>>::from Unexecuted instantiation: <dbus::blocking::LocalConnection as core::convert::From<dbus::channel::ffichannel::Channel>>::from Unexecuted instantiation: <dbus::blocking::SyncConnection as core::convert::From<dbus::channel::ffichannel::Channel>>::from |
292 | | } |
293 | | |
294 | | impl channel::Sender for $c { |
295 | 0 | fn send(&self, msg: Message) -> Result<u32, ()> { self.channel.send(msg) }Unexecuted instantiation: <dbus::blocking::Connection as dbus::channel::Sender>::send Unexecuted instantiation: <dbus::blocking::LocalConnection as dbus::channel::Sender>::send Unexecuted instantiation: <dbus::blocking::SyncConnection as dbus::channel::Sender>::send |
296 | | } |
297 | | |
298 | | impl<S: ReadAll, F: FnMut(S, &$c, &Message) -> bool $(+ $ss)* + 'static> MakeSignal<$cb, S, $c> for F { |
299 | 0 | fn make(mut self, mstr: String) -> $cb { |
300 | 0 | Box::new(move |msg: Message, conn: &$c| { |
301 | 0 | if let Ok(s) = S::read(&mut msg.iter_init()) { |
302 | 0 | if self(s, conn, &msg) { return true }; |
303 | 0 | let proxy = stdintf::proxy(conn); |
304 | | use crate::blocking::stdintf::org_freedesktop::DBus; |
305 | 0 | let _ = proxy.remove_match(&mstr); |
306 | 0 | false |
307 | 0 | } else { true } |
308 | 0 | }) Unexecuted instantiation: <_ as dbus::blocking::MakeSignal<alloc::boxed::Box<dyn for<'a> core::ops::function::FnMut<(dbus::message::Message, &'a dbus::blocking::Connection), Output = bool> + core::marker::Send>, _, dbus::blocking::Connection>>::make::{closure#0}Unexecuted instantiation: <_ as dbus::blocking::MakeSignal<alloc::boxed::Box<dyn for<'a> core::ops::function::FnMut<(dbus::message::Message, &'a dbus::blocking::LocalConnection), Output = bool>>, _, dbus::blocking::LocalConnection>>::make::{closure#0}Unexecuted instantiation: <_ as dbus::blocking::MakeSignal<alloc::boxed::Box<dyn for<'a> core::ops::function::FnMut<(dbus::message::Message, &'a dbus::blocking::SyncConnection), Output = bool> + core::marker::Sync + core::marker::Send>, _, dbus::blocking::SyncConnection>>::make::{closure#0} |
309 | 0 | } Unexecuted instantiation: <_ as dbus::blocking::MakeSignal<alloc::boxed::Box<dyn for<'a> core::ops::function::FnMut<(dbus::message::Message, &'a dbus::blocking::Connection), Output = bool> + core::marker::Send>, _, dbus::blocking::Connection>>::make Unexecuted instantiation: <_ as dbus::blocking::MakeSignal<alloc::boxed::Box<dyn for<'a> core::ops::function::FnMut<(dbus::message::Message, &'a dbus::blocking::LocalConnection), Output = bool>>, _, dbus::blocking::LocalConnection>>::make Unexecuted instantiation: <_ as dbus::blocking::MakeSignal<alloc::boxed::Box<dyn for<'a> core::ops::function::FnMut<(dbus::message::Message, &'a dbus::blocking::SyncConnection), Output = bool> + core::marker::Sync + core::marker::Send>, _, dbus::blocking::SyncConnection>>::make |
310 | | } |
311 | | |
312 | | impl channel::MatchingReceiver for $c { |
313 | | type F = $cb; |
314 | 0 | fn start_receive(&self, m: MatchRule<'static>, f: Self::F) -> Token { |
315 | 0 | self.filters_mut().add(m, f) |
316 | 0 | } Unexecuted instantiation: <dbus::blocking::Connection as dbus::channel::MatchingReceiver>::start_receive Unexecuted instantiation: <dbus::blocking::LocalConnection as dbus::channel::MatchingReceiver>::start_receive Unexecuted instantiation: <dbus::blocking::SyncConnection as dbus::channel::MatchingReceiver>::start_receive |
317 | 0 | fn stop_receive(&self, id: Token) -> Option<(MatchRule<'static>, Self::F)> { |
318 | 0 | self.filters_mut().remove(id) |
319 | 0 | } Unexecuted instantiation: <dbus::blocking::Connection as dbus::channel::MatchingReceiver>::stop_receive Unexecuted instantiation: <dbus::blocking::LocalConnection as dbus::channel::MatchingReceiver>::stop_receive Unexecuted instantiation: <dbus::blocking::SyncConnection as dbus::channel::MatchingReceiver>::stop_receive |
320 | | } |
321 | | |
322 | | |
323 | | |
324 | | } |
325 | | } |
326 | | |
327 | | connimpl!(Connection, FilterCb, Send); |
328 | | connimpl!(LocalConnection, LocalFilterCb); |
329 | | connimpl!(SyncConnection, SyncFilterCb, Send, Sync); |
330 | | |
331 | | impl Connection { |
332 | 0 | fn filters_mut(&self) -> std::cell::RefMut<Filters<FilterCb>> { self.filters.borrow_mut() } |
333 | | } |
334 | | |
335 | | impl LocalConnection { |
336 | 0 | fn filters_mut(&self) -> std::cell::RefMut<Filters<LocalFilterCb>> { self.filters.borrow_mut() } |
337 | | } |
338 | | |
339 | | impl SyncConnection { |
340 | 0 | fn filters_mut(&self) -> std::sync::MutexGuard<Filters<SyncFilterCb>> { self.filters.lock().unwrap() } |
341 | | } |
342 | | |
343 | | /// Abstraction over different connections |
344 | | pub trait BlockingSender { |
345 | | /// Sends a message over the D-Bus and blocks, waiting for a reply or a timeout. This is used for method calls. |
346 | | /// |
347 | | /// Note: In case of an error reply, this is returned as an Err(), not as a Ok(Message) with the error type. |
348 | | fn send_with_reply_and_block(&self, msg: Message, timeout: Duration) -> Result<Message, Error>; |
349 | | } |
350 | | |
351 | | impl BlockingSender for Channel { |
352 | 0 | fn send_with_reply_and_block(&self, msg: Message, timeout: Duration) -> Result<Message, Error> { |
353 | 0 | Channel::send_with_reply_and_block(self, msg, timeout) |
354 | 0 | } |
355 | | } |
356 | | |
357 | | /// A struct that wraps a connection, destination and path. |
358 | | /// |
359 | | /// A D-Bus "Proxy" is a client-side object that corresponds to a remote object on the server side. |
360 | | /// Calling methods on the proxy object calls methods on the remote object. |
361 | | /// Read more in the [D-Bus tutorial](https://dbus.freedesktop.org/doc/dbus-tutorial.html#proxies) |
362 | | #[derive(Clone, Debug)] |
363 | | pub struct Proxy<'a, C> { |
364 | | /// Destination, i e what D-Bus service you're communicating with |
365 | | pub destination: BusName<'a>, |
366 | | /// Object path on the destination |
367 | | pub path: Path<'a>, |
368 | | /// Timeout for method calls |
369 | | pub timeout: Duration, |
370 | | /// Some way to send and/or receive messages, either blocking or non-blocking. |
371 | | pub connection: C, |
372 | | } |
373 | | |
374 | | impl<'a, C> Proxy<'a, C> { |
375 | | /// Creates a new proxy struct. |
376 | 0 | pub fn new<D: Into<BusName<'a>>, P: Into<Path<'a>>>(dest: D, path: P, timeout: Duration, connection: C) -> Self { |
377 | 0 | Proxy { destination: dest.into(), path: path.into(), timeout, connection } |
378 | 0 | } Unexecuted instantiation: <dbus::blocking::Proxy<&dbus::blocking::Connection>>::new::<&str, &str> Unexecuted instantiation: <dbus::blocking::Proxy<&dbus::blocking::SyncConnection>>::new::<&str, &str> Unexecuted instantiation: <dbus::blocking::Proxy<&dbus::blocking::LocalConnection>>::new::<&str, &str> |
379 | | } |
380 | | |
381 | | impl<'a, T: BlockingSender, C: std::ops::Deref<Target=T>> Proxy<'a, C> { |
382 | | // impl<'a, S: std::convert::AsRef<channel::Sender>> Proxy<'a, S> { |
383 | | /// Make a method call using typed input and output arguments, then block waiting for a reply. |
384 | | /// |
385 | | /// # Example |
386 | | /// |
387 | | /// ``` |
388 | | /// use dbus::blocking::{Connection, Proxy}; |
389 | | /// |
390 | | /// let conn = Connection::new_session()?; |
391 | | /// let proxy = Proxy::new("org.freedesktop.DBus", "/", std::time::Duration::from_millis(5000), &conn); |
392 | | /// let (has_owner,): (bool,) = proxy.method_call("org.freedesktop.DBus", "NameHasOwner", ("dummy.name.without.owner",))?; |
393 | | /// assert_eq!(has_owner, false); |
394 | | /// # Ok::<(), Box<dyn std::error::Error>>(()) |
395 | | /// ``` |
396 | 0 | pub fn method_call<'i, 'm, R: ReadAll, A: AppendAll, I: Into<Interface<'i>>, M: Into<Member<'m>>>(&self, i: I, m: M, args: A) -> Result<R, Error> { |
397 | 0 | let mut msg = Message::method_call(&self.destination, &self.path, &i.into(), &m.into()); |
398 | 0 | args.append(&mut IterAppend::new(&mut msg)); |
399 | 0 | let r = self.connection.send_with_reply_and_block(msg, self.timeout)?; |
400 | 0 | Ok(R::read(&mut r.iter_init())?) |
401 | 0 | } Unexecuted instantiation: <dbus::blocking::Proxy<&dbus::blocking::Connection>>::method_call::<(), (&str,), &str, &str> Unexecuted instantiation: <dbus::blocking::Proxy<&dbus::blocking::SyncConnection>>::method_call::<(), (&str,), &str, &str> Unexecuted instantiation: <dbus::blocking::Proxy<&dbus::blocking::LocalConnection>>::method_call::<(), (&str,), &str, &str> |
402 | | |
403 | | /// Starts matching incoming messages on this destination and path. |
404 | | /// |
405 | | /// For matching signals, match_signal might be more convenient. |
406 | | /// |
407 | | /// The match rule will be modified to include this path and destination only. |
408 | | /// |
409 | | /// If call_add_match is true, will notify the D-Bus server that matching should start. |
410 | 0 | pub fn match_start(&self, mut mr: MatchRule<'static>, call_add_match: bool, f: <T as channel::MatchingReceiver>::F) |
411 | 0 | -> Result<Token, Error> |
412 | 0 | where T: channel::MatchingReceiver { |
413 | 0 | mr.path = Some(self.path.clone().into_static()); |
414 | 0 | mr.sender = Some(self.destination.clone().into_static()); |
415 | 0 | if call_add_match { |
416 | | use crate::blocking::stdintf::org_freedesktop::DBus; |
417 | 0 | let proxy = stdintf::proxy(&*self.connection); |
418 | 0 | proxy.add_match(&mr.match_str())?; |
419 | 0 | } |
420 | | |
421 | 0 | Ok(self.connection.start_receive(mr, f)) |
422 | 0 | } |
423 | | |
424 | | /// Stops matching a signal added with match_start or match_signal. |
425 | | /// |
426 | | /// If call_remove_match is true, will notify the D-Bus server that matching should stop, |
427 | | /// this should be true in case match_signal was used. |
428 | 0 | pub fn match_stop(&self, id: Token, call_remove_match: bool) -> Result<(), Error> |
429 | 0 | where T: channel::MatchingReceiver { |
430 | 0 | if let Some((mr, _)) = self.connection.stop_receive(id) { |
431 | 0 | if call_remove_match { |
432 | | use crate::blocking::stdintf::org_freedesktop::DBus; |
433 | 0 | let proxy = stdintf::proxy(&*self.connection); |
434 | 0 | proxy.remove_match(&mr.match_str())?; |
435 | 0 | } |
436 | 0 | } |
437 | 0 | Ok(()) |
438 | 0 | } |
439 | | |
440 | | /// Sets up an incoming signal match, that calls the supplied callback every time the signal is received. |
441 | | /// |
442 | | /// The returned value can be used to remove the match. The match is also removed if the callback |
443 | | /// returns "false". |
444 | 0 | pub fn match_signal<S: SignalArgs + ReadAll, F>(&self, f: F) -> Result<Token, Error> |
445 | 0 | where T: channel::MatchingReceiver, |
446 | 0 | F: MakeSignal<<T as channel::MatchingReceiver>::F, S, T> |
447 | | { |
448 | 0 | let mr = S::match_rule(Some(&self.destination), Some(&self.path)).static_clone(); |
449 | 0 | let ff = f.make(mr.match_str()); |
450 | 0 | self.match_start(mr, true, ff) |
451 | 0 | } |
452 | | } |
453 | | |
454 | | /// Internal helper trait |
455 | | pub trait MakeSignal<G, S, T> { |
456 | | /// Internal helper trait |
457 | | fn make(self, mstr: String) -> G; |
458 | | } |
459 | | |
460 | | #[test] |
461 | | fn test_add_match() { |
462 | | use self::stdintf::org_freedesktop_dbus::PropertiesPropertiesChanged as Ppc; |
463 | | let c = Connection::new_session().unwrap(); |
464 | | let x = c.add_match(Ppc::match_rule(None, None), |_: Ppc, _, _| { true }).unwrap(); |
465 | | c.remove_match(x).unwrap(); |
466 | | } |
467 | | |
468 | | #[test] |
469 | | fn test_conn_send_sync() { |
470 | | fn is_send<T: Send>(_: &T) {} |
471 | | fn is_sync<T: Sync>(_: &T) {} |
472 | | |
473 | | let c = SyncConnection::new_session().unwrap(); |
474 | | is_send(&c); |
475 | | is_sync(&c); |
476 | | |
477 | | let c = Connection::new_session().unwrap(); |
478 | | is_send(&c); |
479 | | } |
480 | | |
481 | | #[test] |
482 | | fn test_peer() { |
483 | | let c = Connection::new_session().unwrap(); |
484 | | |
485 | | let c_name = c.unique_name().into_static(); |
486 | | use std::sync::Arc; |
487 | | let done = Arc::new(false); |
488 | | let d2 = done.clone(); |
489 | | let j = std::thread::spawn(move || { |
490 | | let c2 = Connection::new_session().unwrap(); |
491 | | |
492 | | let proxy = c2.with_proxy(c_name, "/", Duration::from_secs(5)); |
493 | | let (s2,): (String,) = proxy.method_call("org.freedesktop.DBus.Peer", "GetMachineId", ()).unwrap(); |
494 | | println!("{}", s2); |
495 | | assert_eq!(Arc::strong_count(&d2), 2); |
496 | | s2 |
497 | | }); |
498 | | assert_eq!(Arc::strong_count(&done), 2); |
499 | | |
500 | | for _ in 0..30 { |
501 | | c.process(Duration::from_millis(100)).unwrap(); |
502 | | if Arc::strong_count(&done) < 2 { break; } |
503 | | } |
504 | | |
505 | | let s2 = j.join().unwrap(); |
506 | | |
507 | | #[cfg(unix)] |
508 | | { |
509 | | let proxy = c.with_proxy("org.a11y.Bus", "/org/a11y/bus", Duration::from_secs(5)); |
510 | | let (s1,): (String,) = proxy.method_call("org.freedesktop.DBus.Peer", "GetMachineId", ()).unwrap(); |
511 | | |
512 | | assert_eq!(s1, s2); |
513 | | } |
514 | | |
515 | | } |