diff --git a/Cargo.lock b/Cargo.lock index 2c5a3911..32ca2a0d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -844,6 +844,7 @@ name = "cosmic-applet-battery" version = "0.1.0" dependencies = [ "cosmic-time", + "drm 0.11.1", "futures", "i18n-embed 0.13.9", "i18n-embed-fl 0.6.7", @@ -1606,21 +1607,44 @@ checksum = "97fb1b703ffbc7ebd216eba7900008049a56ace55580ecb2ee7fa801e8d8be87" dependencies = [ "bitflags 2.4.2", "bytemuck", - "drm-ffi", + "drm-ffi 0.6.0", "drm-fourcc", "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]] name = "drm-ffi" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba7d1c19c4b6270e89d59fb27dc6d02a317c658a8a54e54781e1db9b5947595d" dependencies = [ - "drm-sys", + "drm-sys 0.5.0", "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]] name = "drm-fourcc" version = "2.2.0" @@ -1633,6 +1657,16 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "either" version = "1.9.0" @@ -3241,6 +3275,12 @@ version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" +[[package]] +name = "linux-raw-sys" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0b5399f6804fbab912acbd8878ed3532d506b7c951b8f9f164ef90fef39e3f4" + [[package]] name = "locale_config" version = "0.3.0" @@ -4804,7 +4844,7 @@ dependencies = [ "cfg_aliases", "cocoa", "core-graphics", - "drm", + "drm 0.10.0", "fastrand 2.0.1", "foreign-types", "js-sys", diff --git a/cosmic-applet-battery/Cargo.toml b/cosmic-applet-battery/Cargo.toml index 1d1413e7..6ad00010 100644 --- a/cosmic-applet-battery/Cargo.toml +++ b/cosmic-applet-battery/Cargo.toml @@ -18,4 +18,5 @@ i18n-embed = { version = "0.13.4", features = ["fluent-system", "desktop-request i18n-embed-fl = "0.6.4" rust-embed = "6.3.0" tokio = { version = "1.17.0", features = ["sync", "rt", "rt-multi-thread", "fs", "macros"] } -udev = "0.8" \ No newline at end of file +udev = "0.8" +drm = "0.11.1" \ No newline at end of file diff --git a/cosmic-applet-battery/src/app.rs b/cosmic-applet-battery/src/app.rs index fbd39573..009aea24 100644 --- a/cosmic-applet-battery/src/app.rs +++ b/cosmic-applet-battery/src/app.rs @@ -2,7 +2,7 @@ use crate::backlight::{ screen_backlight_subscription, ScreenBacklightRequest, ScreenBacklightUpdate, }; use crate::config; -use crate::dgpu::{dgpu_subscription, App, GpuUpdate}; +use crate::dgpu::{dgpu_subscription, Entry, GpuUpdate}; use crate::fl; use crate::power_daemon::{ power_profile_subscription, Power, PowerProfileRequest, PowerProfileUpdate, @@ -65,7 +65,7 @@ static MAX_CHARGE: Lazy = Lazy::new(id::Toggler::unique); struct GPUData { name: String, toggled: bool, - app_list: Option>, + app_list: Option>, } #[derive(Clone, Default)] @@ -159,7 +159,7 @@ enum Message { UpdateScreenBrightness(f64), InitKbdBacklight(UnboundedSender, f64), InitScreenBacklight(UnboundedSender, f64), - GpuOn(PathBuf, String, Option>), + GpuOn(PathBuf, String, Option>), GpuOff(PathBuf), ToggleGpuApps(PathBuf), Errored(String), diff --git a/cosmic-applet-battery/src/dgpu.rs b/cosmic-applet-battery/src/dgpu.rs index 405d5016..31ee7a30 100644 --- a/cosmic-applet-battery/src/dgpu.rs +++ b/cosmic-applet-battery/src/dgpu.rs @@ -3,12 +3,13 @@ use std::{ fmt::{self, Debug}, hash::Hash, io, - os::fd::AsRawFd, + os::fd::{AsFd, AsRawFd}, path::{Path, PathBuf}, time::Duration, }; use cosmic::iced::{self, subscription}; +use drm::control::Device as ControlDevice; use futures::{FutureExt, SinkExt}; use tokio::{ io::unix::AsyncFd, @@ -126,7 +127,7 @@ impl GpuMonitor { } #[derive(Debug, Clone)] -pub struct App { +pub struct Entry { pub name: String, pub icon: Option, pub secondary: String, @@ -140,7 +141,45 @@ pub struct RunningApp { } impl Gpu { - async fn app_list(&self, running_apps: &[RunningApp]) -> Option> { + async fn connected_outputs(&self) -> Option> { + 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> { match self.driver.as_ref().and_then(|s| s.to_str()) { Some("nvidia") => { // figure out bus path for calling nvidia-smi @@ -190,13 +229,13 @@ impl Gpu { .iter() .find(|running_app| running_app.executable_name == process_name) { - App { + Entry { name: application.name.clone(), icon: application.icon.clone(), secondary: String::new(), } } else { - App { + Entry { name: process_name.to_string(), icon: None, secondary: pid.to_string(), @@ -235,13 +274,13 @@ impl Gpu { .iter() .find(|running_app| running_app.executable_name == executable) { - Some(App { + Some(Entry { name: application.name.clone(), icon: application.icon.clone(), secondary: String::new(), }) } else { - Some(App { + Some(Entry { name: executable, icon: None, secondary: pid.to_string(), @@ -351,7 +390,7 @@ pub enum State { #[derive(Debug)] pub enum GpuUpdate { Off(PathBuf), - On(PathBuf, String, Option>), + On(PathBuf, String, Option>), } async fn start_listening( @@ -440,8 +479,12 @@ async fn start_listening( } if enabled { - let app_list = gpu.app_list(&[]).await; - if output.send(GpuUpdate::On(gpu.path.clone(), gpu.name.clone(), app_list)).await.is_err() { + let mut list = gpu.connected_outputs().await.unwrap_or_default(); + 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; } } else if output.send(GpuUpdate::Off(gpu.path.clone())).await.is_err() {