1
0
Fork 0
forked from wry/wry

control-center: add GPUs pane

This commit is contained in:
Julian Orth 2026-02-23 20:40:00 +01:00
parent d328655f8b
commit db06d719dd
7 changed files with 170 additions and 11 deletions

View file

@ -588,7 +588,6 @@ pub trait BackendDrmDevice {
fn set_flip_margin(&self, margin: u64) { fn set_flip_margin(&self, margin: u64) {
let _ = margin; let _ = margin;
} }
#[expect(dead_code)]
fn flip_margin(&self) -> Option<u64> { fn flip_margin(&self) -> Option<u64> {
None None
} }

View file

@ -951,8 +951,10 @@ impl ConfigProxyHandler {
} }
fn handle_set_flip_margin(&self, device: DrmDevice, margin: Duration) -> Result<(), CphError> { fn handle_set_flip_margin(&self, device: DrmDevice, margin: Duration) -> Result<(), CphError> {
self.get_drm_device(device)? self.get_drm_device(device)?.set_flip_margin(
.set_flip_margin(margin.as_nanos().try_into().unwrap_or(u64::MAX)); &self.state,
margin.as_nanos().try_into().unwrap_or(u64::MAX),
);
Ok(()) Ok(())
} }
@ -987,7 +989,7 @@ impl ConfigProxyHandler {
match device { match device {
Some(dev) => self Some(dev) => self
.get_drm_device(dev)? .get_drm_device(dev)?
.set_direct_scanout_enabled(enabled), .set_direct_scanout_enabled(&self.state, enabled),
_ => self.state.direct_scanout_enabled.set(enabled), _ => self.state.direct_scanout_enabled.set(enabled),
} }
Ok(()) Ok(())

View file

@ -2,7 +2,8 @@ use {
crate::{ crate::{
control_center::{ control_center::{
cc_color_management::ColorManagementPane, cc_compositor::CompositorPane, cc_color_management::ColorManagementPane, cc_compositor::CompositorPane,
cc_idle::IdlePane, cc_outputs::OutputsPane, cc_xwayland::XwaylandPane, cc_gpus::GpusPane, cc_idle::IdlePane, cc_outputs::OutputsPane,
cc_xwayland::XwaylandPane,
}, },
egui_adapter::egui_platform::{ egui_adapter::egui_platform::{
EggError, EggWindow, EggWindowOwner, EggError, EggWindow, EggWindowOwner,
@ -35,6 +36,7 @@ use {
mod cc_color_management; mod cc_color_management;
mod cc_compositor; mod cc_compositor;
mod cc_gpus;
mod cc_idle; mod cc_idle;
mod cc_outputs; mod cc_outputs;
mod cc_sidebar; mod cc_sidebar;
@ -76,6 +78,7 @@ bitflags! {
CCI_COLOR_MANAGEMENT, CCI_COLOR_MANAGEMENT,
CCI_XWAYLAND, CCI_XWAYLAND,
CCI_OUTPUTS, CCI_OUTPUTS,
CCI_GPUS,
} }
pub struct ControlCenter { pub struct ControlCenter {
@ -121,6 +124,7 @@ enum PaneType {
ColorManagement(ColorManagementPane), ColorManagement(ColorManagementPane),
Xwayland(XwaylandPane), Xwayland(XwaylandPane),
Outputs(Box<OutputsPane>), Outputs(Box<OutputsPane>),
GPUs(GpusPane),
} }
struct CcBehavior<'a> { struct CcBehavior<'a> {
@ -144,6 +148,7 @@ impl Pane {
PaneType::ColorManagement(v) => v.title(res), PaneType::ColorManagement(v) => v.title(res),
PaneType::Xwayland(v) => v.title(res), PaneType::Xwayland(v) => v.title(res),
PaneType::Outputs(v) => v.title(res), PaneType::Outputs(v) => v.title(res),
PaneType::GPUs(v) => v.title(res),
} }
} }
@ -154,6 +159,7 @@ impl Pane {
PaneType::ColorManagement(p) => p.show(ui), PaneType::ColorManagement(p) => p.show(ui),
PaneType::Xwayland(p) => p.show(behavior, ui), PaneType::Xwayland(p) => p.show(behavior, ui),
PaneType::Outputs(p) => p.show(&mut self.ps, ui), PaneType::Outputs(p) => p.show(&mut self.ps, ui),
PaneType::GPUs(p) => p.show(ui),
} }
} }
} }
@ -166,6 +172,7 @@ impl PaneType {
PaneType::ColorManagement(_) => CCI_COLOR_MANAGEMENT, PaneType::ColorManagement(_) => CCI_COLOR_MANAGEMENT,
PaneType::Xwayland(_) => CCI_XWAYLAND, PaneType::Xwayland(_) => CCI_XWAYLAND,
PaneType::Outputs(_) => CCI_OUTPUTS, PaneType::Outputs(_) => CCI_OUTPUTS,
PaneType::GPUs(_) => CCI_GPUS,
} }
} }
} }

View file

@ -0,0 +1,146 @@
use {
crate::{
control_center::{
ControlCenterInner, GridExt, bool, combo_box, grid, grid_label, label, row,
},
egui_adapter::egui_platform::icons::{ICON_ADD, ICON_REMOVE},
state::{DrmDevData, State},
},
egui::{Checkbox, CollapsingHeader, DragValue, TextFormat, Ui, Widget, text::LayoutJob},
std::rc::Rc,
};
pub struct GpusPane {
state: Rc<State>,
}
impl ControlCenterInner {
pub fn create_gpus_pane(self: &Rc<Self>) -> GpusPane {
GpusPane {
state: self.state.clone(),
}
}
}
impl GpusPane {
pub fn title(&self, res: &mut String) {
res.push_str("GPUs");
}
pub fn show(&mut self, ui: &mut Ui) {
let devs = self.state.drm_devs.lock();
let mut devs: Vec<_> = devs.iter().collect();
devs.sort_by_key(|d| d.0);
for dev in devs {
self.show_dev(ui, dev.1);
}
}
fn show_dev(&self, ui: &mut Ui, dev: &DrmDevData) {
let title_buf;
let title = match dev.devnode.as_deref() {
Some(t) => t,
_ => {
let dev_t = dev.dev.dev_t();
title_buf = format!("{}:{}", uapi::major(dev_t), uapi::minor(dev_t));
&title_buf
}
};
let mut layout_job = LayoutJob::default();
layout_job.append(
title,
0.0,
TextFormat {
color: ui.style().visuals.widgets.active.text_color(),
..Default::default()
},
);
if let Some(model) = &dev.model {
layout_job.append(
model,
10.0,
TextFormat {
color: ui.style().visuals.widgets.inactive.text_color(),
..Default::default()
},
);
}
ui.collapsing(layout_job, |ui| {
grid(ui, ("settings", dev.dev.id()), |ui| {
macro_rules! string {
($field:ident, $name:expr) => {
if let Some(v) = &dev.$field {
label(ui, $name, v);
}
};
}
string!(vendor, "Vendor");
string!(model, "Model");
string!(devnode, "Devnode");
string!(syspath, "Syspath");
if let Some(v) = dev.pci_id {
label(ui, "PCI ID", format!("{:x}:{:x}", v.vendor, v.model));
}
{
let v = dev.dev.dev_t();
label(ui, "Dev", format!("{}:{}", uapi::major(v), uapi::minor(v)));
}
combo_box(ui, "API", dev.dev.gtx_api(), |v| dev.dev.set_gfx_api(v));
row(ui, "Primary Device", |ui| {
let mut v = dev.dev.is_render_device();
let old = v;
ui.add_enabled(!v, Checkbox::without_text(&mut v));
if v != old {
dev.dev.make_render_device();
}
});
bool(
ui,
"Direct Scanout",
dev.dev.direct_scanout_enabled(),
|v| dev.set_direct_scanout_enabled(&self.state, v),
);
if let Some(mut v) = dev.dev.flip_margin() {
let ui = &mut *ui.row();
grid_label(ui, "Flip Margin");
let old = v;
let denom = 1_000_000.0;
ui.horizontal(|ui| {
let mut s = v as f64 / denom;
let res = DragValue::new(&mut s)
.range(0.0..=50.0)
.speed(0.1)
.fixed_decimals(1)
.ui(ui);
if res.changed() {
v = (s * denom) as u64;
}
if ui.button(ICON_REMOVE).clicked() {
v = v.saturating_sub(100_000);
}
if ui.button(ICON_ADD).clicked() {
v += 100_000;
}
});
if old != v {
dev.set_flip_margin(&self.state, v);
}
}
});
CollapsingHeader::new("Connectors")
.default_open(true)
.show(ui, |ui| {
let mut cs: Vec<_> = dev
.connectors
.lock()
.values()
.map(|v| v.connector.kernel_id().to_string())
.collect::<Vec<_>>();
cs.sort();
for c in cs {
ui.label(c);
}
});
});
}
}

View file

@ -13,6 +13,7 @@ enum PaneName {
ColorManagement, ColorManagement,
Xwayland, Xwayland,
Outputs, Outputs,
GPUs,
} }
impl PaneName { impl PaneName {
@ -23,6 +24,7 @@ impl PaneName {
PaneName::ColorManagement => "Color Management", PaneName::ColorManagement => "Color Management",
PaneName::Xwayland => "Xwayland", PaneName::Xwayland => "Xwayland",
PaneName::Outputs => "Outputs", PaneName::Outputs => "Outputs",
PaneName::GPUs => "GPUs",
} }
} }
} }
@ -60,6 +62,7 @@ impl ControlCenterInner {
PaneName::Outputs => { PaneName::Outputs => {
PaneType::Outputs(Box::new(self.create_outputs_pane())) PaneType::Outputs(Box::new(self.create_outputs_pane()))
} }
PaneName::GPUs => PaneType::GPUs(self.create_gpus_pane()),
}; };
self.open(tree, ty); self.open(tree, ty);
ui.ctx().request_repaint(); ui.ctx().request_repaint();

View file

@ -350,7 +350,7 @@ impl JayRandrRequestHandler for JayRandr {
let Some(dev) = self.get_device(req.dev) else { let Some(dev) = self.get_device(req.dev) else {
return Ok(()); return Ok(());
}; };
dev.set_direct_scanout_enabled(req.enabled != 0); dev.set_direct_scanout_enabled(&self.state, req.enabled != 0);
Ok(()) Ok(())
} }
@ -493,7 +493,7 @@ impl JayRandrRequestHandler for JayRandr {
let Some(dev) = self.get_device(req.dev) else { let Some(dev) = self.get_device(req.dev) else {
return Ok(()); return Ok(());
}; };
dev.set_flip_margin(req.margin_ns); dev.set_flip_margin(&self.state, req.margin_ns);
Ok(()) Ok(())
} }

View file

@ -17,7 +17,7 @@ use {
compositor::{LIBEI_SOCKET, LogLevel}, compositor::{LIBEI_SOCKET, LogLevel},
config::ConfigProxy, config::ConfigProxy,
control_center::{ control_center::{
CCI_COLOR_MANAGEMENT, CCI_COMPOSITOR, CCI_IDLE, CCI_OUTPUTS, CCI_XWAYLAND, CCI_COLOR_MANAGEMENT, CCI_COMPOSITOR, CCI_GPUS, CCI_IDLE, CCI_OUTPUTS, CCI_XWAYLAND,
ControlCenters, ControlCenters,
}, },
copy_device::CopyDeviceRegistry, copy_device::CopyDeviceRegistry,
@ -549,12 +549,14 @@ impl DrmDevData {
self.dev.clone().make_render_device(); self.dev.clone().make_render_device();
} }
pub fn set_direct_scanout_enabled(&self, enabled: bool) { pub fn set_direct_scanout_enabled(&self, state: &State, enabled: bool) {
self.dev.set_direct_scanout_enabled(enabled); self.dev.set_direct_scanout_enabled(enabled);
state.trigger_cci(CCI_GPUS);
} }
pub fn set_flip_margin(&self, margin: u64) { pub fn set_flip_margin(&self, state: &State, margin: u64) {
self.dev.set_flip_margin(margin); self.dev.set_flip_margin(margin);
state.trigger_cci(CCI_GPUS);
} }
} }
@ -778,7 +780,7 @@ impl State {
} }
self.expose_new_singletons(); self.expose_new_singletons();
self.trigger_cci(CCI_COLOR_MANAGEMENT); self.trigger_cci(CCI_COLOR_MANAGEMENT | CCI_GPUS);
} }
fn reload_cursors(&self) { fn reload_cursors(&self) {