Skip to content

Commit 580571c

Browse files
committed
Expose DBus API with DeviceVisibility property
1 parent 03814e0 commit 580571c

File tree

8 files changed

+181
-2
lines changed

8 files changed

+181
-2
lines changed

Cargo.lock

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,4 @@ ashpd = { version = "0.12", default-features = false, features = [
4343
futures-timer = "3.0.3"
4444
tokio-util = "0.7.15"
4545
tracing-appender = "0.2.3"
46+
const-str = "0.7.0"

data/meson.build

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,3 +99,14 @@ configure_file(
9999
install: true,
100100
install_dir: datadir / 'dbus-1' / 'services'
101101
)
102+
103+
service_conf = configuration_data()
104+
service_conf.set('app-id', api_id)
105+
service_conf.set('bindir', bindir)
106+
configure_file(
107+
input: '@0@.service.in'.format(base_id),
108+
output: '@0@.service'.format(api_id),
109+
configuration: service_conf,
110+
install: true,
111+
install_dir: datadir / 'dbus-1' / 'services'
112+
)

meson.build

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ else
4848
application_id = base_id
4949
endif
5050

51+
api_id = application_id + '.Api'
52+
5153
meson.add_dist_script(
5254
'build-aux/dist-vendor.sh',
5355
meson.project_build_root() / 'meson-dist' / meson.project_name()
@@ -70,4 +72,4 @@ gnome.post_install(
7072
gtk_update_icon_cache: true,
7173
glib_compile_schemas: true,
7274
update_desktop_database: true,
73-
)
75+
)

src/config.rs.in

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,6 @@ pub const PKGDATADIR: &str = @PKGDATADIR@;
66
pub const PROFILE: &str = @PROFILE@;
77
pub const RESOURCES_FILE: &str = concat!(@PKGDATADIR@, "/resources.gresource");
88
pub const VERSION: &str = @VERSION@;
9+
10+
pub const DBUS_API_NAME: &str = const_str::concat!(APP_ID, ".Api");
11+
pub const DBUS_API_PATH: &str = const_str::concat!("/", const_str::replace!(APP_ID, ".", "/"));

src/dbus.rs

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
use std::sync::{Arc, LazyLock};
2+
3+
use tokio::sync::{
4+
Mutex,
5+
mpsc::{Receiver, Sender, channel},
6+
};
7+
use zbus::{Connection, object_server::InterfaceRef};
8+
9+
use crate::config::{DBUS_API_NAME, DBUS_API_PATH};
10+
11+
#[derive(Debug)]
12+
pub struct Packet {
13+
pub visibility: bool,
14+
visibility_tx: Arc<Mutex<Sender<bool>>>,
15+
pub visibility_rx: Arc<Mutex<Receiver<bool>>>,
16+
}
17+
18+
#[zbus::interface(name = "io.github.nozwock.Packet1")]
19+
impl Packet {
20+
#[zbus(property)]
21+
pub async fn device_visibility(&self) -> bool {
22+
self.visibility
23+
}
24+
25+
/// Also sends the param to the channel associated with `visibility_tx`.
26+
#[zbus(property)]
27+
pub async fn set_device_visibility(&mut self, visibility: bool) {
28+
self.visibility = visibility;
29+
_ = self
30+
.visibility_tx
31+
.lock()
32+
.await
33+
.send(visibility)
34+
.await
35+
.inspect_err(|err| tracing::warn!(%err));
36+
}
37+
}
38+
39+
static CONNECTION: LazyLock<Mutex<Option<Connection>>> = LazyLock::new(|| Mutex::new(None));
40+
41+
pub async fn get_connection() -> Option<Connection> {
42+
CONNECTION.lock().await.as_ref().cloned()
43+
}
44+
45+
pub async fn create_connection(visibility: bool) -> anyhow::Result<Connection> {
46+
let mut conn_guard = CONNECTION.lock().await;
47+
48+
if let Some(conn) = conn_guard.as_ref() {
49+
Ok(conn.clone())
50+
} else {
51+
let (tx, rx) = channel::<bool>(1);
52+
let conn = zbus::connection::Builder::session()?
53+
.name(DBUS_API_NAME)?
54+
.serve_at(
55+
DBUS_API_PATH,
56+
Packet {
57+
visibility: visibility,
58+
visibility_tx: Arc::new(Mutex::new(tx)),
59+
visibility_rx: Arc::new(Mutex::new(rx)),
60+
},
61+
)?
62+
.build()
63+
.await?;
64+
*conn_guard = Some(conn.clone());
65+
66+
Ok(conn)
67+
}
68+
}
69+
70+
/// # Panics
71+
/// Panics if `CONNECTION` is `None`.
72+
pub async fn packet_iface() -> InterfaceRef<Packet> {
73+
get_connection()
74+
.await
75+
.expect("Session should be created before getting iface")
76+
.object_server()
77+
.interface::<_, Packet>(DBUS_API_PATH)
78+
.await
79+
.expect("Interface should be on the object path")
80+
}

src/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ mod application;
22
#[rustfmt::skip]
33
mod config;
44
mod constants;
5+
mod dbus;
56
mod ext;
67
mod monitors;
78
mod objects;

src/window.rs

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ use tokio_util::sync::CancellationToken;
1919
use crate::application::PacketApplication;
2020
use crate::config::{APP_ID, PROFILE};
2121
use crate::constants::packet_log_path;
22+
use crate::dbus;
2223
use crate::ext::MessageExt;
2324
use crate::objects::{self, SendRequestState};
2425
use crate::objects::{TransferState, UserAction};
@@ -171,6 +172,7 @@ mod imp {
171172
pub is_mdns_discovery_on: Rc<Cell<bool>>,
172173

173174
pub looping_async_tasks: RefCell<Vec<LoopingTaskHandle>>,
175+
pub dbus_async_tasks: RefCell<Vec<LoopingTaskHandle>>,
174176

175177
pub is_background_allowed: Cell<bool>,
176178
pub should_quit: Cell<bool>,
@@ -216,6 +218,7 @@ mod imp {
216218
obj.setup_notification_actions_monitor();
217219
obj.setup_rqs_service();
218220
obj.request_background_at_start();
221+
obj.setup_dbus_api();
219222
}
220223
}
221224

@@ -265,9 +268,16 @@ mod imp {
265268

266269
// Abort all looping tasks before closing
267270
tracing::info!(
268-
count = self.looping_async_tasks.borrow().len(),
271+
rqs_tasks = self.looping_async_tasks.borrow().len(),
272+
dbus_tasks = self.dbus_async_tasks.borrow().len(),
269273
"Cancelling looping tasks"
270274
);
275+
while let Some(join_handle) = self.dbus_async_tasks.borrow_mut().pop() {
276+
match join_handle {
277+
LoopingTaskHandle::Tokio(join_handle) => join_handle.abort(),
278+
LoopingTaskHandle::Glib(join_handle) => join_handle.abort(),
279+
}
280+
}
271281
while let Some(join_handle) = self.looping_async_tasks.borrow_mut().pop() {
272282
match join_handle {
273283
LoopingTaskHandle::Tokio(join_handle) => join_handle.abort(),
@@ -1849,6 +1859,70 @@ impl PacketApplicationWindow {
18491859
handle
18501860
}
18511861

1862+
fn setup_dbus_api(&self) {
1863+
let obj = self.clone();
1864+
1865+
let inner = async move || -> anyhow::Result<()> {
1866+
let imp = obj.imp();
1867+
1868+
_ = dbus::create_connection(imp.settings.boolean("device-visibility")).await?;
1869+
1870+
let handle = glib::spawn_future_local(clone!(
1871+
#[weak]
1872+
imp,
1873+
async move {
1874+
// IMPORTANT: Keep notice of get() and get_mut() so that it doesn't lead to deadlock
1875+
let mut visibility_rx = {
1876+
let iface_ref = dbus::packet_iface().await;
1877+
iface_ref
1878+
.get()
1879+
.await
1880+
.visibility_rx
1881+
.clone()
1882+
.lock_owned()
1883+
.await
1884+
};
1885+
1886+
while let Some(extern_visibility) = visibility_rx.recv().await {
1887+
_ = imp
1888+
.settings
1889+
.set_boolean("device-visibility", extern_visibility)
1890+
.inspect_err(|err| tracing::warn!(%err));
1891+
}
1892+
}
1893+
));
1894+
imp.dbus_async_tasks
1895+
.borrow_mut()
1896+
.push(LoopingTaskHandle::Glib(handle));
1897+
1898+
imp.settings
1899+
.connect_changed(Some("device-visibility"), move |settings, key| {
1900+
let key = key.to_string();
1901+
glib::spawn_future_local(clone!(
1902+
#[weak]
1903+
settings,
1904+
async move {
1905+
let iface_ref = dbus::packet_iface().await;
1906+
let mut iface = iface_ref.get_mut().await;
1907+
iface.visibility = settings.boolean(&key);
1908+
_ = iface
1909+
.device_visibility_changed(iface_ref.signal_emitter())
1910+
.await
1911+
.inspect_err(|err| tracing::warn!(%err));
1912+
}
1913+
));
1914+
});
1915+
1916+
Ok(())
1917+
};
1918+
1919+
glib::spawn_future_local(async move {
1920+
_ = inner()
1921+
.await
1922+
.inspect_err(|err| tracing::error!(%err, "Failed to setup DBus API"));
1923+
});
1924+
}
1925+
18521926
fn setup_connection_monitors(&self) {
18531927
let imp = self.imp();
18541928

0 commit comments

Comments
 (0)