Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature/fix: implement periodic update for connected devices #965

Merged
merged 2 commits into from
Feb 12, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions cosmic-settings/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ rustix = "0.38.41"
gettext-rs = { version = "0.7.2", features = [
"gettext-system",
], optional = true }
async-fn-stream = "0.2.2"

[dependencies.cosmic-settings-subscriptions]
git = "https://github.com/pop-os/cosmic-settings-subscriptions"
Expand Down
54 changes: 51 additions & 3 deletions cosmic-settings/src/pages/power/backend/mod.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
use chrono::{Duration, TimeDelta};
use futures::future::join_all;
use futures::FutureExt;
use futures::{future::join_all, FutureExt, Stream, StreamExt};
use upower_dbus::{BatteryState, BatteryType, DeviceProxy};
use zbus::Connection;
use zbus::{zvariant::ObjectPath, Connection};

mod ppdaemon;
mod s76powerdaemon;
Expand Down Expand Up @@ -239,6 +238,7 @@ pub struct ConnectedDevice {
pub model: String,
pub device_icon: &'static str,
pub battery: Battery,
pub device_path: String,
}

async fn get_device_proxy<'a>() -> Result<upower_dbus::DeviceProxy<'a>, zbus::Error> {
Expand Down Expand Up @@ -406,6 +406,7 @@ impl Battery {
impl ConnectedDevice {
async fn from_device_maybe(proxy: DeviceProxy<'_>) -> Option<Self> {
let device_type = proxy.type_().await.unwrap_or(BatteryType::Unknown);
let device_path = proxy.clone().into_inner().path().to_string();
if matches!(
device_type,
BatteryType::Unknown | BatteryType::LinePower | BatteryType::Battery
Expand Down Expand Up @@ -445,9 +446,56 @@ impl ConnectedDevice {
model,
device_icon,
battery,
device_path,
})
}

pub async fn device_removed_stream(
connection: &'_ Connection,
) -> Result<impl Stream<Item = String> + '_, zbus::Error> {
let proxy = upower_dbus::UPowerProxy::new(connection).await?;
let stream = proxy.receive_device_removed().await?;

let transformed_stream = stream.filter_map(move |device_removed| async move {
let device_path: ObjectPath<'static> = match device_removed.args() {
Ok(args) => args.device().to_owned(),
Err(e) => {
tracing::error!("Failed to get DeviceRemoved arguments: {e}");
return None;
}
};
Some(device_path.to_string())
});

Ok(transformed_stream)
}

pub async fn device_added_stream(
connection: &'_ Connection,
) -> Result<impl futures::Stream<Item = ConnectedDevice> + '_, zbus::Error> {
let proxy = upower_dbus::UPowerProxy::new(connection).await?;
let stream = proxy.receive_device_added().await?;

let transformed_stream = stream.filter_map(move |device_added| async move {
let device_path: ObjectPath<'static> = match device_added.args() {
Ok(args) => args.device().to_owned(),
Err(e) => {
tracing::error!("Failed to get DeviceAdded arguments: {e}");
return None;
}
};
match DeviceProxy::new(connection, &device_path).await {
Ok(device) => ConnectedDevice::from_device_maybe(device).await,
Err(e) => {
tracing::error!("Failed to create DeviceProxy from {device_path}: {e}");
None
}
}
});

Ok(transformed_stream)
}

pub async fn update_connected_devices() -> Vec<Self> {
let proxy = enumerate_devices().await;

Expand Down
55 changes: 55 additions & 0 deletions cosmic-settings/src/pages/power/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use cosmic::Task;
use cosmic_config::{Config, CosmicConfigEntry};
use cosmic_idle_config::CosmicIdleConfig;
use cosmic_settings_page::{self as page, section, Section};
use futures::StreamExt;
use itertools::Itertools;
use slab::Slab;
use slotmap::SlotMap;
Expand Down Expand Up @@ -123,6 +124,52 @@ impl page::Page<crate::pages::Message> for Page {
let devices = ConnectedDevice::update_connected_devices().await;
Message::UpdateConnectedDevices(devices)
}),
cosmic::Task::run(
async_fn_stream::fn_stream(|emitter| async move {
let span = tracing::span!(tracing::Level::INFO, "power::device_stream task");
let _span_handle = span.enter();

let Ok(connection) = zbus::Connection::system().await else {
tracing::error!("could not established zbus connection to system");
return;
};

let added_stream = ConnectedDevice::device_added_stream(&connection).await;
let removed_stream = ConnectedDevice::device_removed_stream(&connection).await;

let added_future = async {
match added_stream {
Ok(stream) => {
futures::pin_mut!(stream);
while let Some(device) = stream.next().await {
tracing::info!(device = device.model, "device added");
emitter.emit(Message::DeviceConnect(device)).await;
}
}
Err(err) => tracing::error!(?err, "cannot establish added stream"),
}
};

let removed_future = async {
match removed_stream {
Ok(stream) => {
futures::pin_mut!(stream);
while let Some(device_path) = stream.next().await {
tracing::info!(device_path, "device removed");
emitter.emit(Message::DeviceDisconnect(device_path)).await;
}
}
Err(err) => tracing::error!(?err, "cannot establish removed stream"),
}
};

futures::pin_mut!(added_future);
futures::pin_mut!(removed_future);

futures::future::select(added_future, removed_future).await;
}),
|msg| msg,
),
];

let (task, handle) = cosmic::Task::batch(futures)
Expand All @@ -147,6 +194,8 @@ pub enum Message {
PowerProfileChange(PowerProfile),
UpdateBattery(Battery),
UpdateConnectedDevices(Vec<ConnectedDevice>),
DeviceDisconnect(String),
DeviceConnect(ConnectedDevice),
ScreenOffTimeChange(Option<Duration>),
SuspendOnAcTimeChange(Option<Duration>),
SuspendOnBatteryTimeChange(Option<Duration>),
Expand Down Expand Up @@ -192,6 +241,12 @@ impl Page {
tracing::error!("failed to set suspend on battery time: {}", err)
}
}
Message::DeviceDisconnect(device_path) => self
.connected_devices
.retain(|device| device.device_path != device_path),
Message::DeviceConnect(connected_device) => {
self.connected_devices.push(connected_device)
}
};
}
}
Expand Down