battery/dgpu: Add connected outputs to list

This commit is contained in:
Victoria Brekenfeld 2024-01-31 18:53:31 +01:00
parent 8472f1f358
commit 2a47cde1b4
4 changed files with 101 additions and 17 deletions

46
Cargo.lock generated
View file

@ -844,6 +844,7 @@ name = "cosmic-applet-battery"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"cosmic-time", "cosmic-time",
"drm 0.11.1",
"futures", "futures",
"i18n-embed 0.13.9", "i18n-embed 0.13.9",
"i18n-embed-fl 0.6.7", "i18n-embed-fl 0.6.7",
@ -1606,21 +1607,44 @@ checksum = "97fb1b703ffbc7ebd216eba7900008049a56ace55580ecb2ee7fa801e8d8be87"
dependencies = [ dependencies = [
"bitflags 2.4.2", "bitflags 2.4.2",
"bytemuck", "bytemuck",
"drm-ffi", "drm-ffi 0.6.0",
"drm-fourcc", "drm-fourcc",
"nix 0.27.1", "nix 0.27.1",
] ]
[[package]]
name = "drm"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0f8a69e60d75ae7dab4ef26a59ca99f2a89d4c142089b537775ae0c198bdcde"
dependencies = [
"bitflags 2.4.2",
"bytemuck",
"drm-ffi 0.7.1",
"drm-fourcc",
"rustix 0.38.30",
]
[[package]] [[package]]
name = "drm-ffi" name = "drm-ffi"
version = "0.6.0" version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba7d1c19c4b6270e89d59fb27dc6d02a317c658a8a54e54781e1db9b5947595d" checksum = "ba7d1c19c4b6270e89d59fb27dc6d02a317c658a8a54e54781e1db9b5947595d"
dependencies = [ dependencies = [
"drm-sys", "drm-sys 0.5.0",
"nix 0.27.1", "nix 0.27.1",
] ]
[[package]]
name = "drm-ffi"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41334f8405792483e32ad05fbb9c5680ff4e84491883d2947a4757dc54cb2ac6"
dependencies = [
"drm-sys 0.6.1",
"rustix 0.38.30",
]
[[package]] [[package]]
name = "drm-fourcc" name = "drm-fourcc"
version = "2.2.0" version = "2.2.0"
@ -1633,6 +1657,16 @@ version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a4f1c0468062a56cd5705f1e3b5409eb286d5596a2028ec8e947595d7e715ae" checksum = "3a4f1c0468062a56cd5705f1e3b5409eb286d5596a2028ec8e947595d7e715ae"
[[package]]
name = "drm-sys"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d09ff881f92f118b11105ba5e34ff8f4adf27b30dae8f12e28c193af1c83176"
dependencies = [
"libc",
"linux-raw-sys 0.6.4",
]
[[package]] [[package]]
name = "either" name = "either"
version = "1.9.0" version = "1.9.0"
@ -3241,6 +3275,12 @@ version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c"
[[package]]
name = "linux-raw-sys"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0b5399f6804fbab912acbd8878ed3532d506b7c951b8f9f164ef90fef39e3f4"
[[package]] [[package]]
name = "locale_config" name = "locale_config"
version = "0.3.0" version = "0.3.0"
@ -4804,7 +4844,7 @@ dependencies = [
"cfg_aliases", "cfg_aliases",
"cocoa", "cocoa",
"core-graphics", "core-graphics",
"drm", "drm 0.10.0",
"fastrand 2.0.1", "fastrand 2.0.1",
"foreign-types", "foreign-types",
"js-sys", "js-sys",

View file

@ -18,4 +18,5 @@ i18n-embed = { version = "0.13.4", features = ["fluent-system", "desktop-request
i18n-embed-fl = "0.6.4" i18n-embed-fl = "0.6.4"
rust-embed = "6.3.0" rust-embed = "6.3.0"
tokio = { version = "1.17.0", features = ["sync", "rt", "rt-multi-thread", "fs", "macros"] } tokio = { version = "1.17.0", features = ["sync", "rt", "rt-multi-thread", "fs", "macros"] }
udev = "0.8" udev = "0.8"
drm = "0.11.1"

View file

@ -2,7 +2,7 @@ use crate::backlight::{
screen_backlight_subscription, ScreenBacklightRequest, ScreenBacklightUpdate, screen_backlight_subscription, ScreenBacklightRequest, ScreenBacklightUpdate,
}; };
use crate::config; use crate::config;
use crate::dgpu::{dgpu_subscription, App, GpuUpdate}; use crate::dgpu::{dgpu_subscription, Entry, GpuUpdate};
use crate::fl; use crate::fl;
use crate::power_daemon::{ use crate::power_daemon::{
power_profile_subscription, Power, PowerProfileRequest, PowerProfileUpdate, power_profile_subscription, Power, PowerProfileRequest, PowerProfileUpdate,
@ -65,7 +65,7 @@ static MAX_CHARGE: Lazy<id::Toggler> = Lazy::new(id::Toggler::unique);
struct GPUData { struct GPUData {
name: String, name: String,
toggled: bool, toggled: bool,
app_list: Option<Vec<App>>, app_list: Option<Vec<Entry>>,
} }
#[derive(Clone, Default)] #[derive(Clone, Default)]
@ -159,7 +159,7 @@ enum Message {
UpdateScreenBrightness(f64), UpdateScreenBrightness(f64),
InitKbdBacklight(UnboundedSender<KeyboardBacklightRequest>, f64), InitKbdBacklight(UnboundedSender<KeyboardBacklightRequest>, f64),
InitScreenBacklight(UnboundedSender<ScreenBacklightRequest>, f64), InitScreenBacklight(UnboundedSender<ScreenBacklightRequest>, f64),
GpuOn(PathBuf, String, Option<Vec<App>>), GpuOn(PathBuf, String, Option<Vec<Entry>>),
GpuOff(PathBuf), GpuOff(PathBuf),
ToggleGpuApps(PathBuf), ToggleGpuApps(PathBuf),
Errored(String), Errored(String),

View file

@ -3,12 +3,13 @@ use std::{
fmt::{self, Debug}, fmt::{self, Debug},
hash::Hash, hash::Hash,
io, io,
os::fd::AsRawFd, os::fd::{AsFd, AsRawFd},
path::{Path, PathBuf}, path::{Path, PathBuf},
time::Duration, time::Duration,
}; };
use cosmic::iced::{self, subscription}; use cosmic::iced::{self, subscription};
use drm::control::Device as ControlDevice;
use futures::{FutureExt, SinkExt}; use futures::{FutureExt, SinkExt};
use tokio::{ use tokio::{
io::unix::AsyncFd, io::unix::AsyncFd,
@ -126,7 +127,7 @@ impl GpuMonitor {
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct App { pub struct Entry {
pub name: String, pub name: String,
pub icon: Option<String>, pub icon: Option<String>,
pub secondary: String, pub secondary: String,
@ -140,7 +141,45 @@ pub struct RunningApp {
} }
impl Gpu { impl Gpu {
async fn app_list(&self, running_apps: &[RunningApp]) -> Option<Vec<App>> { async fn connected_outputs(&self) -> Option<Vec<Entry>> {
let path = self.path.clone();
spawn_blocking(move || {
struct Device(std::fs::File);
impl AsFd for Device {
fn as_fd(&self) -> std::os::unix::prelude::BorrowedFd<'_> {
self.0.as_fd()
}
}
impl drm::Device for Device {}
impl ControlDevice for Device {}
let device = Device(std::fs::File::open(path).ok()?);
let resources = device.resource_handles().ok()?;
let outputs = resources
.connectors
.into_iter()
.filter_map(|conn| device.get_connector(conn, false).ok())
.filter(|info| info.state() == drm::control::connector::State::Connected)
.map(|info| Entry {
name: format!(
"Output @ {}:{}",
info.interface().as_str(),
info.interface_id()
),
icon: Some("display-symbolic".to_string()),
secondary: String::new(),
})
.collect();
// TODO read and parse edid with libdisplay-info and display output manufacture/model
Some(outputs)
})
.await
.ok()?
}
async fn app_list(&self, running_apps: &[RunningApp]) -> Option<Vec<Entry>> {
match self.driver.as_ref().and_then(|s| s.to_str()) { match self.driver.as_ref().and_then(|s| s.to_str()) {
Some("nvidia") => { Some("nvidia") => {
// figure out bus path for calling nvidia-smi // figure out bus path for calling nvidia-smi
@ -190,13 +229,13 @@ impl Gpu {
.iter() .iter()
.find(|running_app| running_app.executable_name == process_name) .find(|running_app| running_app.executable_name == process_name)
{ {
App { Entry {
name: application.name.clone(), name: application.name.clone(),
icon: application.icon.clone(), icon: application.icon.clone(),
secondary: String::new(), secondary: String::new(),
} }
} else { } else {
App { Entry {
name: process_name.to_string(), name: process_name.to_string(),
icon: None, icon: None,
secondary: pid.to_string(), secondary: pid.to_string(),
@ -235,13 +274,13 @@ impl Gpu {
.iter() .iter()
.find(|running_app| running_app.executable_name == executable) .find(|running_app| running_app.executable_name == executable)
{ {
Some(App { Some(Entry {
name: application.name.clone(), name: application.name.clone(),
icon: application.icon.clone(), icon: application.icon.clone(),
secondary: String::new(), secondary: String::new(),
}) })
} else { } else {
Some(App { Some(Entry {
name: executable, name: executable,
icon: None, icon: None,
secondary: pid.to_string(), secondary: pid.to_string(),
@ -351,7 +390,7 @@ pub enum State {
#[derive(Debug)] #[derive(Debug)]
pub enum GpuUpdate { pub enum GpuUpdate {
Off(PathBuf), Off(PathBuf),
On(PathBuf, String, Option<Vec<App>>), On(PathBuf, String, Option<Vec<Entry>>),
} }
async fn start_listening( async fn start_listening(
@ -440,8 +479,12 @@ async fn start_listening(
} }
if enabled { if enabled {
let app_list = gpu.app_list(&[]).await; let mut list = gpu.connected_outputs().await.unwrap_or_default();
if output.send(GpuUpdate::On(gpu.path.clone(), gpu.name.clone(), app_list)).await.is_err() { if let Some(mut apps) = gpu.app_list(&[]).await {
apps.retain(|app| app.name != "cosmic-comp");
list.extend(apps);
}
if output.send(GpuUpdate::On(gpu.path.clone(), gpu.name.clone(), (!list.is_empty()).then_some(list))).await.is_err() {
return State::Finished; return State::Finished;
} }
} else if output.send(GpuUpdate::Off(gpu.path.clone())).await.is_err() { } else if output.send(GpuUpdate::Off(gpu.path.clone())).await.is_err() {