Skip to content

Commit c76fd47

Browse files
authored
feat(power): implement periodic update for connected devices
1 parent 442589f commit c76fd47

File tree

4 files changed

+118
-3
lines changed

4 files changed

+118
-3
lines changed

Cargo.lock

+11
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cosmic-settings/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ rustix = "0.38.41"
7474
gettext-rs = { version = "0.7.2", features = [
7575
"gettext-system",
7676
], optional = true }
77+
async-fn-stream = "0.2.2"
7778

7879
[dependencies.cosmic-settings-subscriptions]
7980
git = "https://github.com/pop-os/cosmic-settings-subscriptions"

cosmic-settings/src/pages/power/backend/mod.rs

+51-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
use chrono::{Duration, TimeDelta};
2-
use futures::future::join_all;
3-
use futures::FutureExt;
2+
use futures::{future::join_all, FutureExt, Stream, StreamExt};
43
use upower_dbus::{BatteryState, BatteryType, DeviceProxy};
5-
use zbus::Connection;
4+
use zbus::{zvariant::ObjectPath, Connection};
65

76
mod ppdaemon;
87
mod s76powerdaemon;
@@ -239,6 +238,7 @@ pub struct ConnectedDevice {
239238
pub model: String,
240239
pub device_icon: &'static str,
241240
pub battery: Battery,
241+
pub device_path: String,
242242
}
243243

244244
async fn get_device_proxy<'a>() -> Result<upower_dbus::DeviceProxy<'a>, zbus::Error> {
@@ -406,6 +406,7 @@ impl Battery {
406406
impl ConnectedDevice {
407407
async fn from_device_maybe(proxy: DeviceProxy<'_>) -> Option<Self> {
408408
let device_type = proxy.type_().await.unwrap_or(BatteryType::Unknown);
409+
let device_path = proxy.clone().into_inner().path().to_string();
409410
if matches!(
410411
device_type,
411412
BatteryType::Unknown | BatteryType::LinePower | BatteryType::Battery
@@ -445,9 +446,56 @@ impl ConnectedDevice {
445446
model,
446447
device_icon,
447448
battery,
449+
device_path,
448450
})
449451
}
450452

453+
pub async fn device_removed_stream(
454+
connection: &'_ Connection,
455+
) -> Result<impl Stream<Item = String> + '_, zbus::Error> {
456+
let proxy = upower_dbus::UPowerProxy::new(connection).await?;
457+
let stream = proxy.receive_device_removed().await?;
458+
459+
let transformed_stream = stream.filter_map(move |device_removed| async move {
460+
let device_path: ObjectPath<'static> = match device_removed.args() {
461+
Ok(args) => args.device().to_owned(),
462+
Err(e) => {
463+
tracing::error!("Failed to get DeviceRemoved arguments: {e}");
464+
return None;
465+
}
466+
};
467+
Some(device_path.to_string())
468+
});
469+
470+
Ok(transformed_stream)
471+
}
472+
473+
pub async fn device_added_stream(
474+
connection: &'_ Connection,
475+
) -> Result<impl futures::Stream<Item = ConnectedDevice> + '_, zbus::Error> {
476+
let proxy = upower_dbus::UPowerProxy::new(connection).await?;
477+
let stream = proxy.receive_device_added().await?;
478+
479+
let transformed_stream = stream.filter_map(move |device_added| async move {
480+
let device_path: ObjectPath<'static> = match device_added.args() {
481+
Ok(args) => args.device().to_owned(),
482+
Err(e) => {
483+
tracing::error!("Failed to get DeviceAdded arguments: {e}");
484+
return None;
485+
}
486+
};
487+
match DeviceProxy::new(connection, &device_path).await {
488+
Ok(device) => ConnectedDevice::from_device_maybe(device).await,
489+
Err(e) => {
490+
tracing::error!("Failed to create DeviceProxy from {device_path}: {e}");
491+
None
492+
}
493+
}
494+
});
495+
496+
Ok(transformed_stream)
497+
}
498+
451499
pub async fn update_connected_devices() -> Vec<Self> {
452500
let proxy = enumerate_devices().await;
453501

cosmic-settings/src/pages/power/mod.rs

+55
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use cosmic::Task;
1212
use cosmic_config::{Config, CosmicConfigEntry};
1313
use cosmic_idle_config::CosmicIdleConfig;
1414
use cosmic_settings_page::{self as page, section, Section};
15+
use futures::StreamExt;
1516
use itertools::Itertools;
1617
use slab::Slab;
1718
use slotmap::SlotMap;
@@ -123,6 +124,52 @@ impl page::Page<crate::pages::Message> for Page {
123124
let devices = ConnectedDevice::update_connected_devices().await;
124125
Message::UpdateConnectedDevices(devices)
125126
}),
127+
cosmic::Task::run(
128+
async_fn_stream::fn_stream(|emitter| async move {
129+
let span = tracing::span!(tracing::Level::INFO, "power::device_stream task");
130+
let _span_handle = span.enter();
131+
132+
let Ok(connection) = zbus::Connection::system().await else {
133+
tracing::error!("could not established zbus connection to system");
134+
return;
135+
};
136+
137+
let added_stream = ConnectedDevice::device_added_stream(&connection).await;
138+
let removed_stream = ConnectedDevice::device_removed_stream(&connection).await;
139+
140+
let added_future = async {
141+
match added_stream {
142+
Ok(stream) => {
143+
futures::pin_mut!(stream);
144+
while let Some(device) = stream.next().await {
145+
tracing::info!(device = device.model, "device added");
146+
emitter.emit(Message::DeviceConnect(device)).await;
147+
}
148+
}
149+
Err(err) => tracing::error!(?err, "cannot establish added stream"),
150+
}
151+
};
152+
153+
let removed_future = async {
154+
match removed_stream {
155+
Ok(stream) => {
156+
futures::pin_mut!(stream);
157+
while let Some(device_path) = stream.next().await {
158+
tracing::info!(device_path, "device removed");
159+
emitter.emit(Message::DeviceDisconnect(device_path)).await;
160+
}
161+
}
162+
Err(err) => tracing::error!(?err, "cannot establish removed stream"),
163+
}
164+
};
165+
166+
futures::pin_mut!(added_future);
167+
futures::pin_mut!(removed_future);
168+
169+
futures::future::select(added_future, removed_future).await;
170+
}),
171+
|msg| msg,
172+
),
126173
];
127174

128175
let (task, handle) = cosmic::Task::batch(futures)
@@ -147,6 +194,8 @@ pub enum Message {
147194
PowerProfileChange(PowerProfile),
148195
UpdateBattery(Battery),
149196
UpdateConnectedDevices(Vec<ConnectedDevice>),
197+
DeviceDisconnect(String),
198+
DeviceConnect(ConnectedDevice),
150199
ScreenOffTimeChange(Option<Duration>),
151200
SuspendOnAcTimeChange(Option<Duration>),
152201
SuspendOnBatteryTimeChange(Option<Duration>),
@@ -192,6 +241,12 @@ impl Page {
192241
tracing::error!("failed to set suspend on battery time: {}", err)
193242
}
194243
}
244+
Message::DeviceDisconnect(device_path) => self
245+
.connected_devices
246+
.retain(|device| device.device_path != device_path),
247+
Message::DeviceConnect(connected_device) => {
248+
self.connected_devices.push(connected_device)
249+
}
195250
};
196251
}
197252
}

0 commit comments

Comments
 (0)