mirror of
https://github.com/ilya-zlobintsev/LACT.git
synced 2025-02-25 18:55:26 -06:00
feat: somewhat working ui
This commit is contained in:
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -26,7 +26,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "amdgpu-sysfs"
|
||||
version = "0.5.0"
|
||||
source = "git+https://github.com/ilya-zlobintsev/amdgpu-sysfs-rs?branch=blocking#55ddcc95cddde27e5c5147d9b5ee4831b5d7aaf8"
|
||||
source = "git+https://github.com/ilya-zlobintsev/amdgpu-sysfs-rs?branch=blocking#0308d4c475f5091f7e8bc4b7b5d8c101f8dd40d7"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
@@ -80,7 +80,8 @@ impl App {
|
||||
});
|
||||
}
|
||||
|
||||
let devices = self.daemon_client.list_devices()?;
|
||||
let devices_buf = self.daemon_client.list_devices()?;
|
||||
let devices = devices_buf.inner()?;
|
||||
self.header.set_devices(&devices);
|
||||
|
||||
// Show apply button on setting changes
|
||||
@@ -96,10 +97,10 @@ impl App {
|
||||
|
||||
let apply_revealer = self.apply_revealer.clone();
|
||||
|
||||
self.root_stack.oc_page.connect_settings_changed(move || {
|
||||
debug!("Settings changed, showing apply button");
|
||||
apply_revealer.show();
|
||||
});
|
||||
// self.root_stack.oc_page.connect_settings_changed(move || {
|
||||
// debug!("Settings changed, showing apply button");
|
||||
// apply_revealer.show();
|
||||
// });
|
||||
}
|
||||
|
||||
{
|
||||
@@ -185,13 +186,17 @@ impl App {
|
||||
}
|
||||
|
||||
fn set_info(&self, gpu_id: &str) {
|
||||
let info = self.daemon_client.get_device_info(gpu_id).unwrap();
|
||||
let info_buf = self
|
||||
.daemon_client
|
||||
.get_device_info(gpu_id)
|
||||
.expect("Could not fetch info");
|
||||
let info = info_buf.inner().unwrap();
|
||||
trace!("Setting info {info:?}");
|
||||
|
||||
self.root_stack.info_page.set_info(&info);
|
||||
|
||||
trace!("Setting clocks");
|
||||
self.root_stack.oc_page.set_info(&info);
|
||||
// trace!("Setting clocks");
|
||||
// self.root_stack.oc_page.set_info(&info);
|
||||
|
||||
// TODO: this should be stats
|
||||
/*trace!("Setting performance level {:?}", info.power_profile);
|
||||
@@ -221,15 +226,15 @@ impl App {
|
||||
let ppfeaturemask: u64 =
|
||||
u64::from_str_radix(ppfeaturemask, 16).expect("Invalid ppfeaturemask");
|
||||
|
||||
if (ppfeaturemask & PP_OVERDRIVE_MASK as u64) > 0 {
|
||||
/*if (ppfeaturemask & PP_OVERDRIVE_MASK as u64) > 0 {
|
||||
self.root_stack.oc_page.warning_frame.hide();
|
||||
} else {
|
||||
self.root_stack.oc_page.warning_frame.show();
|
||||
}
|
||||
}*/
|
||||
}
|
||||
Err(_) => {
|
||||
info!("Failed to read feature mask! This is expected if your system doesn't have an AMD GPU.");
|
||||
self.root_stack.oc_page.warning_frame.hide();
|
||||
// self.root_stack.oc_page.warning_frame.hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -249,7 +254,10 @@ impl App {
|
||||
|
||||
thread::spawn(move || loop {
|
||||
let gpu_id = current_gpu_id.read().unwrap();
|
||||
match daemon_connection.get_device_stats(&gpu_id) {
|
||||
match daemon_connection
|
||||
.get_device_stats(&gpu_id)
|
||||
.and_then(|stats| stats.inner())
|
||||
{
|
||||
Ok(stats) => {
|
||||
sender.send(GuiUpdateMsg::GpuStats(stats)).unwrap();
|
||||
}
|
||||
@@ -264,14 +272,14 @@ impl App {
|
||||
// Receiving stats into the gui event loop
|
||||
{
|
||||
let thermals_page = self.root_stack.thermals_page.clone();
|
||||
let oc_page = self.root_stack.oc_page.clone();
|
||||
// let oc_page = self.root_stack.oc_page.clone();
|
||||
|
||||
receiver.attach(None, move |msg| {
|
||||
match msg {
|
||||
GuiUpdateMsg::GpuStats(stats) => {
|
||||
trace!("New stats received, updating {stats:?}");
|
||||
thermals_page.set_stats(&stats);
|
||||
oc_page.set_stats(&stats);
|
||||
// oc_page.set_stats(&stats);
|
||||
} /*GuiUpdateMsg::FanControlInfo(fan_control_info) => {
|
||||
thermals_page.set_ventilation_info(fan_control_info)
|
||||
}*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
mod info_page;
|
||||
mod oc_page;
|
||||
// mod oc_page;
|
||||
mod software_page;
|
||||
mod thermals_page;
|
||||
|
||||
@@ -7,7 +7,7 @@ use gtk::prelude::*;
|
||||
use gtk::*;
|
||||
|
||||
use info_page::InformationPage;
|
||||
use oc_page::OcPage;
|
||||
// use oc_page::OcPage;
|
||||
use software_page::SoftwarePage;
|
||||
use thermals_page::ThermalsPage;
|
||||
|
||||
@@ -17,7 +17,7 @@ pub struct RootStack {
|
||||
pub info_page: InformationPage,
|
||||
pub thermals_page: ThermalsPage,
|
||||
pub software_page: SoftwarePage,
|
||||
pub oc_page: OcPage,
|
||||
// pub oc_page: OcPage,
|
||||
}
|
||||
|
||||
impl RootStack {
|
||||
@@ -28,9 +28,9 @@ impl RootStack {
|
||||
|
||||
container.add_titled(&info_page.container, "info_page", "Information");
|
||||
|
||||
let oc_page = OcPage::new();
|
||||
// let oc_page = OcPage::new();
|
||||
|
||||
container.add_titled(&oc_page.container, "oc_page", "OC");
|
||||
// container.add_titled(&oc_page.container, "oc_page", "OC");
|
||||
|
||||
let thermals_page = ThermalsPage::new();
|
||||
|
||||
@@ -44,7 +44,7 @@ impl RootStack {
|
||||
container,
|
||||
info_page,
|
||||
thermals_page,
|
||||
oc_page,
|
||||
// oc_page,
|
||||
software_page,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,11 +151,13 @@ impl InformationPage {
|
||||
pub fn set_info(&self, gpu_info: &DeviceInfo) {
|
||||
let gpu_name = gpu_info
|
||||
.pci_info
|
||||
.as_ref()
|
||||
.and_then(|pci_info| {
|
||||
pci_info
|
||||
.subsystem_pci_info
|
||||
.model
|
||||
.or_else(|| pci_info.device_pci_info.model)
|
||||
.as_deref()
|
||||
.or_else(|| pci_info.device_pci_info.model.as_deref())
|
||||
})
|
||||
.unwrap_or_default();
|
||||
self.gpu_name_label
|
||||
@@ -163,43 +165,48 @@ impl InformationPage {
|
||||
|
||||
let gpu_manufacturer = gpu_info
|
||||
.pci_info
|
||||
.as_ref()
|
||||
.and_then(|pci_info| {
|
||||
pci_info
|
||||
.subsystem_pci_info
|
||||
.vendor
|
||||
.or_else(|| pci_info.device_pci_info.model)
|
||||
.as_deref()
|
||||
.or_else(|| pci_info.device_pci_info.model.as_deref())
|
||||
})
|
||||
.unwrap_or_default();
|
||||
self.gpu_manufacturer_label
|
||||
.set_markup(&format!("<b>{gpu_manufacturer}</b>",));
|
||||
|
||||
let vbios_version = gpu_info.vbios_version.as_deref().unwrap_or("<unknown>");
|
||||
let vbios_version = gpu_info.vbios_version.as_deref().unwrap_or("unknown");
|
||||
self.vbios_version_label
|
||||
.set_markup(&format!("<b>{vbios_version}</b>",));
|
||||
|
||||
self.driver_label
|
||||
.set_markup(&format!("<b>{}</b>", gpu_info.driver));
|
||||
|
||||
let vram_size = gpu_info
|
||||
.vram_size
|
||||
.map_or_else(|| "<unknown>".to_owned(), |size| size.to_string());
|
||||
self.vram_size_label
|
||||
.set_markup(&format!("<b>{vram_size}</b>"));
|
||||
// TODO
|
||||
// let vram_size = gpu_info
|
||||
// .vram_size
|
||||
// .map_or_else(|| "unknown".to_owned(), |size| size.to_string());
|
||||
// self.vram_size_label
|
||||
// .set_markup(&format!("<b>{vram_size}</b>"));
|
||||
|
||||
let link_speed = gpu_info
|
||||
.link_info
|
||||
.current_speed
|
||||
.as_deref()
|
||||
.unwrap_or("<unknown>");
|
||||
.unwrap_or("unknown");
|
||||
let link_width = gpu_info
|
||||
.link_info
|
||||
.current_width
|
||||
.as_deref()
|
||||
.unwrap_or("<unknown>");
|
||||
.unwrap_or("unknown");
|
||||
self.link_speed_label
|
||||
.set_markup(&format!("<b>{link_speed} x{link_width}</b>",));
|
||||
|
||||
self.vulkan_info_frame.set_info(&gpu_info.vulkan_info);
|
||||
if let Some(vulkan_info) = &gpu_info.vulkan_info {
|
||||
self.vulkan_info_frame.set_info(vulkan_info);
|
||||
}
|
||||
|
||||
self.container.show_all();
|
||||
}
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
mod clocks_frame;
|
||||
// mod clocks_frame;
|
||||
mod performance_level_frame;
|
||||
mod power_cap_frame;
|
||||
mod stats_grid;
|
||||
mod warning_frame;
|
||||
|
||||
use amdgpu_sysfs::gpu_handle::PerformanceLevel;
|
||||
use clocks_frame::ClocksFrame;
|
||||
use clocks_frame::ClocksSettings;
|
||||
use gtk::prelude::*;
|
||||
use gtk::*;
|
||||
use lact_schema::{DeviceInfo, DeviceStats};
|
||||
@@ -21,7 +19,7 @@ pub struct OcPage {
|
||||
stats_grid: StatsGrid,
|
||||
performance_level_frame: PowerProfileFrame,
|
||||
power_cap_frame: PowerCapFrame,
|
||||
clocks_frame: ClocksFrame,
|
||||
// clocks_frame: ClocksFrame,
|
||||
pub warning_frame: WarningFrame,
|
||||
}
|
||||
|
||||
@@ -45,15 +43,15 @@ impl OcPage {
|
||||
|
||||
container.pack_start(&power_profile_frame.container, false, true, 0);
|
||||
|
||||
let clocks_frame = ClocksFrame::new();
|
||||
// let clocks_frame = ClocksFrame::new();
|
||||
|
||||
container.pack_start(&clocks_frame.container, false, true, 0);
|
||||
// container.pack_start(&clocks_frame.container, false, true, 0);
|
||||
|
||||
Self {
|
||||
container,
|
||||
stats_grid,
|
||||
performance_level_frame: power_profile_frame,
|
||||
clocks_frame,
|
||||
// clocks_frame,
|
||||
warning_frame,
|
||||
power_cap_frame,
|
||||
}
|
||||
@@ -100,9 +98,12 @@ impl OcPage {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_power_profile(&self) -> Option<PowerProfile> {
|
||||
pub fn get_performance_level(&self) -> Option<PowerProfile> {
|
||||
match self.performance_level_frame.get_visibility() {
|
||||
true => Some(self.performance_level_frame.get_selected_power_profile()),
|
||||
true => Some(
|
||||
self.performance_level_frame
|
||||
.get_selected_performance_level(),
|
||||
),
|
||||
false => None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
use daemon::gpu_controller::ClocksTable;
|
||||
use gtk::prelude::*;
|
||||
use gtk::*;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use daemon::gpu_controller::PowerProfile;
|
||||
use amdgpu_sysfs::gpu_handle::PerformanceLevel;
|
||||
use gtk::prelude::*;
|
||||
use gtk::*;
|
||||
|
||||
@@ -57,11 +57,12 @@ impl PowerProfileFrame {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_active_profile(&self, profile: &PowerProfile) {
|
||||
match profile {
|
||||
PowerProfile::Auto => self.combo_box.set_active_id(Some("0")),
|
||||
PowerProfile::High => self.combo_box.set_active_id(Some("1")),
|
||||
PowerProfile::Low => self.combo_box.set_active_id(Some("2")),
|
||||
pub fn set_active_profile(&self, level: PerformanceLevel) {
|
||||
match level {
|
||||
PerformanceLevel::Auto => self.combo_box.set_active_id(Some("0")),
|
||||
PerformanceLevel::High => self.combo_box.set_active_id(Some("1")),
|
||||
PerformanceLevel::Low => self.combo_box.set_active_id(Some("2")),
|
||||
PerformanceLevel::Manual => todo!(),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -71,11 +72,11 @@ impl PowerProfileFrame {
|
||||
});
|
||||
}
|
||||
|
||||
pub fn get_selected_power_profile(&self) -> PowerProfile {
|
||||
pub fn get_selected_performance_level(&self) -> PerformanceLevel {
|
||||
match self.combo_box.active().unwrap() {
|
||||
0 => PowerProfile::Auto,
|
||||
1 => PowerProfile::High,
|
||||
2 => PowerProfile::Low,
|
||||
0 => PerformanceLevel::Auto,
|
||||
1 => PerformanceLevel::High,
|
||||
2 => PerformanceLevel::Low,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use gtk::prelude::*;
|
||||
use gtk::*;
|
||||
use std::collections::BTreeMap;
|
||||
use tracing::debug;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct FanCurveFrame {
|
||||
@@ -189,12 +189,12 @@ impl FanCurveFrame {
|
||||
}
|
||||
|
||||
pub fn show(&self) {
|
||||
log::info!("Manual fan control enaged, showing fan curve");
|
||||
debug!("Manual fan control enaged, showing fan curve");
|
||||
self.container.set_visible(true);
|
||||
}
|
||||
|
||||
pub fn hide(&self) {
|
||||
log::info!("Manual fan control disenaged, hiding fan curve");
|
||||
debug!("Manual fan control disenaged, hiding fan curve");
|
||||
self.container.set_visible(false);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
use anyhow::{anyhow, Context};
|
||||
use lact_schema::{request::Request, response::Response, DeviceInfo, DeviceListEntry, DeviceStats};
|
||||
use nix::unistd::getuid;
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde::{de::DeserializeOwned, Deserialize};
|
||||
use std::{
|
||||
io::{BufRead, BufReader, Write},
|
||||
marker::PhantomData,
|
||||
ops::DerefMut,
|
||||
os::unix::net::UnixStream,
|
||||
path::PathBuf,
|
||||
sync::{Arc, Mutex},
|
||||
@@ -26,8 +28,12 @@ impl DaemonClient {
|
||||
})
|
||||
}
|
||||
|
||||
fn make_request<T: DeserializeOwned>(&self, request: Request) -> anyhow::Result<T> {
|
||||
let (reader, writer) = *self.stream.lock().map_err(|err| anyhow!("{err}"))?;
|
||||
fn make_request<'a, T: Deserialize<'a>>(
|
||||
&self,
|
||||
request: Request,
|
||||
) -> anyhow::Result<ResponseBuffer<T>> {
|
||||
let mut stream_guard = self.stream.lock().map_err(|err| anyhow!("{err}"))?;
|
||||
let (reader, writer) = stream_guard.deref_mut();
|
||||
|
||||
if !reader.buffer().is_empty() {
|
||||
return Err(anyhow!("Another request was not processed properly"));
|
||||
@@ -38,30 +44,29 @@ impl DaemonClient {
|
||||
writer.write_all(b"\n")?;
|
||||
|
||||
let mut response_payload = String::new();
|
||||
reader.read_line(&mut response_payload);
|
||||
reader.read_line(&mut response_payload)?;
|
||||
|
||||
let response: Response<T> = serde_json::from_str(&response_payload)
|
||||
.context("Could not deserialize response from daemon")?;
|
||||
|
||||
match response {
|
||||
Response::Ok(data) => Ok(data),
|
||||
Response::Error(error) => Err(anyhow!("Error from daemon: {error}")),
|
||||
}
|
||||
Ok(ResponseBuffer {
|
||||
buf: response_payload,
|
||||
_phantom: PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn list_devices<'a>(&self) -> anyhow::Result<Vec<DeviceListEntry<'a>>> {
|
||||
pub fn list_devices<'a>(&self) -> anyhow::Result<ResponseBuffer<Vec<DeviceListEntry<'a>>>> {
|
||||
self.make_request(Request::ListDevices)
|
||||
}
|
||||
|
||||
pub fn set_fan_control(&self, id: &str, enabled: bool) -> anyhow::Result<()> {
|
||||
self.make_request(Request::SetFanControl { id, enabled })
|
||||
self.make_request::<()>(Request::SetFanControl { id, enabled })?
|
||||
.inner()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_device_info(&self, id: &str) -> anyhow::Result<DeviceInfo> {
|
||||
pub fn get_device_info(&self, id: &str) -> anyhow::Result<ResponseBuffer<DeviceInfo>> {
|
||||
self.make_request(Request::DeviceInfo { id })
|
||||
}
|
||||
|
||||
pub fn get_device_stats(&self, id: &str) -> anyhow::Result<DeviceStats> {
|
||||
pub fn get_device_stats(&self, id: &str) -> anyhow::Result<ResponseBuffer<DeviceStats>> {
|
||||
self.make_request(Request::DeviceStats { id })
|
||||
}
|
||||
}
|
||||
@@ -82,3 +87,19 @@ fn get_socket_path() -> Option<PathBuf> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ResponseBuffer<T> {
|
||||
buf: String,
|
||||
_phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<'a, T: Deserialize<'a>> ResponseBuffer<T> {
|
||||
pub fn inner(&'a self) -> anyhow::Result<T> {
|
||||
let response: Response<T> = serde_json::from_str(&self.buf)
|
||||
.context("Could not deserialize response from daemon")?;
|
||||
match response {
|
||||
Response::Ok(data) => Ok(data),
|
||||
Response::Error(err) => Err(anyhow!("Got error from daemon: {err}")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user