mirror of
https://github.com/ilya-zlobintsev/LACT.git
synced 2025-02-25 18:55:26 -06:00
Initial multi-gpu support
This commit is contained in:
parent
a4a639534c
commit
c78f18eaee
@ -8,4 +8,6 @@ edition = "2018"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
daemon = { path = "../daemon" }
|
daemon = { path = "../daemon" }
|
||||||
structopt = "0.3"
|
structopt = "0.3"
|
||||||
|
log = "0.4"
|
||||||
|
env_logger = "0.8"
|
@ -5,40 +5,58 @@ use structopt::StructOpt;
|
|||||||
#[structopt(rename_all = "lower")]
|
#[structopt(rename_all = "lower")]
|
||||||
enum Opt {
|
enum Opt {
|
||||||
///Gets realtime GPU information
|
///Gets realtime GPU information
|
||||||
Stats,
|
Stats {
|
||||||
Info,
|
gpu_id: u32,
|
||||||
StartFanControl,
|
},
|
||||||
StopFanControl,
|
Gpus,
|
||||||
GetFanControl,
|
Info {
|
||||||
|
gpu_id: u32,
|
||||||
|
},
|
||||||
|
StartFanControl {
|
||||||
|
gpu_id: u32,
|
||||||
|
},
|
||||||
|
StopFanControl {
|
||||||
|
gpu_id: u32,
|
||||||
|
},
|
||||||
|
GetFanControl {
|
||||||
|
gpu_id: u32,
|
||||||
|
},
|
||||||
Stop,
|
Stop,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
env_logger::init();
|
||||||
|
|
||||||
let opt = Opt::from_args();
|
let opt = Opt::from_args();
|
||||||
|
|
||||||
let d = DaemonConnection::new().unwrap();
|
let d = DaemonConnection::new().unwrap();
|
||||||
|
log::trace!("connection established");
|
||||||
|
|
||||||
match opt {
|
match opt {
|
||||||
Opt::Stats => {
|
Opt::Gpus => {
|
||||||
let gpu_stats = d.get_gpu_stats();
|
let gpus = d.get_gpus();
|
||||||
|
println!("{:?}", gpus);
|
||||||
|
},
|
||||||
|
Opt::Stats { gpu_id } => {
|
||||||
|
let gpu_stats = d.get_gpu_stats(gpu_id);
|
||||||
println!("VRAM: {}/{}", gpu_stats.mem_used, gpu_stats.mem_total);
|
println!("VRAM: {}/{}", gpu_stats.mem_used, gpu_stats.mem_total);
|
||||||
println!("{:?}", gpu_stats);
|
println!("{:?}", gpu_stats);
|
||||||
},
|
},
|
||||||
Opt::Info => {
|
Opt::Info { gpu_id } => {
|
||||||
let gpu_info = d.get_gpu_info();
|
let gpu_info = d.get_gpu_info(gpu_id);
|
||||||
println!("GPU Vendor: {}", gpu_info.gpu_vendor);
|
println!("GPU Vendor: {}", gpu_info.gpu_vendor);
|
||||||
println!("GPU Model: {}", gpu_info.card_model);
|
println!("GPU Model: {}", gpu_info.card_model);
|
||||||
println!("Driver in use: {}", gpu_info.driver);
|
println!("Driver in use: {}", gpu_info.driver);
|
||||||
print!("VBIOS Version: {}", gpu_info.vbios_version);
|
print!("VBIOS Version: {}", gpu_info.vbios_version);
|
||||||
},
|
},
|
||||||
Opt::StartFanControl => {
|
Opt::StartFanControl { gpu_id } => {
|
||||||
println!("{:?}", d.start_fan_control());
|
println!("{:?}", d.start_fan_control(gpu_id));
|
||||||
},
|
},
|
||||||
Opt::StopFanControl => {
|
Opt::StopFanControl { gpu_id } => {
|
||||||
println!("{:?}", d.stop_fan_control());
|
println!("{:?}", d.stop_fan_control(gpu_id));
|
||||||
},
|
},
|
||||||
Opt::GetFanControl => {
|
Opt::GetFanControl { gpu_id } => {
|
||||||
println!("{:?}", d.get_fan_control());
|
println!("{:?}", d.get_fan_control(gpu_id));
|
||||||
},
|
},
|
||||||
Opt::Stop => d.shutdown(),
|
Opt::Stop => d.shutdown(),
|
||||||
}
|
}
|
||||||
|
@ -12,4 +12,5 @@ serde = { version = "1.0", features = ["derive"] }
|
|||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
vulkano = "0.19"
|
vulkano = "0.19"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
env_logger = "0.8"
|
env_logger = "0.8"
|
||||||
|
rand = "0.7"
|
@ -1,5 +1,5 @@
|
|||||||
use std::{collections::BTreeMap, io, fs, path::PathBuf};
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::{collections::BTreeMap, fs, io, path::PathBuf};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum ConfigError {
|
pub enum ConfigError {
|
||||||
@ -42,7 +42,7 @@ impl Config {
|
|||||||
|
|
||||||
pub fn read_from_file(path: &PathBuf) -> Result<Self, ConfigError> {
|
pub fn read_from_file(path: &PathBuf) -> Result<Self, ConfigError> {
|
||||||
let json = fs::read_to_string(path)?;
|
let json = fs::read_to_string(path)?;
|
||||||
|
|
||||||
Ok(serde_json::from_str::<Config>(&json)?)
|
Ok(serde_json::from_str::<Config>(&json)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,4 +62,4 @@ mod tests {
|
|||||||
let c = Config::new();
|
let c = Config::new();
|
||||||
c.save(PathBuf::from("/tmp/config.json"))
|
c.save(PathBuf::from("/tmp/config.json"))
|
||||||
}
|
}
|
||||||
}*/
|
}*/
|
||||||
|
@ -1,6 +1,14 @@
|
|||||||
use crate::{Action, SOCK_PATH, gpu_controller::GpuInfo, gpu_controller::{FanControlInfo, GpuStats}, hw_mon::HWMonError};
|
use crate::{
|
||||||
use std::{collections::BTreeMap, io::{Read, Write}};
|
gpu_controller::GpuInfo,
|
||||||
use std::os::unix::net::UnixStream;
|
gpu_controller::{FanControlInfo, GpuStats},
|
||||||
|
hw_mon::HWMonError,
|
||||||
|
Action, SOCK_PATH,
|
||||||
|
};
|
||||||
|
use std::{collections::HashMap, os::unix::net::UnixStream};
|
||||||
|
use std::{
|
||||||
|
collections::BTreeMap,
|
||||||
|
io::{Read, Write},
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum DaemonConnectionError {
|
pub enum DaemonConnectionError {
|
||||||
@ -9,21 +17,26 @@ pub enum DaemonConnectionError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub struct DaemonConnection {
|
pub struct DaemonConnection {}
|
||||||
}
|
|
||||||
|
|
||||||
impl DaemonConnection {
|
impl DaemonConnection {
|
||||||
pub fn new() -> Result<Self, DaemonConnectionError> {
|
pub fn new() -> Result<Self, DaemonConnectionError> {
|
||||||
match UnixStream::connect(SOCK_PATH) {
|
match UnixStream::connect(SOCK_PATH) {
|
||||||
Ok(mut stream) => {
|
Ok(mut stream) => {
|
||||||
stream.write(&bincode::serialize(&Action::CheckAlive).unwrap()).unwrap();
|
stream
|
||||||
let mut buffer = Vec::<u8>::new();
|
.write(&bincode::serialize(&Action::CheckAlive).unwrap())
|
||||||
stream.read_to_end(&mut buffer).unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
stream
|
||||||
|
.shutdown(std::net::Shutdown::Write)
|
||||||
|
.expect("Could not shut down");
|
||||||
|
|
||||||
|
let mut buffer: [u8; 1] = [0; 1];
|
||||||
|
stream.read(&mut buffer).unwrap();
|
||||||
|
|
||||||
if buffer[0] == 1 {
|
if buffer[0] == 1 {
|
||||||
Ok(DaemonConnection { })
|
Ok(DaemonConnection {})
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
Err(DaemonConnectionError::ConnectionFailed)
|
Err(DaemonConnectionError::ConnectionFailed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -31,83 +44,110 @@ impl DaemonConnection {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_gpu_stats(&self) -> GpuStats {
|
pub fn get_gpu_stats(&self, gpu_id: u32) -> GpuStats {
|
||||||
let mut stream = UnixStream::connect(SOCK_PATH).expect("Failed to connect to daemon");
|
let mut s = UnixStream::connect(SOCK_PATH).expect("Failed to connect to daemon");
|
||||||
stream
|
s
|
||||||
.write(&bincode::serialize(&Action::GetStats).unwrap())
|
.write(&bincode::serialize(&Action::GetStats(gpu_id)).unwrap())
|
||||||
.unwrap();
|
|
||||||
/*stream
|
|
||||||
.shutdown(std::net::Shutdown::Write)
|
|
||||||
.expect("Could not shut down");*/
|
|
||||||
|
|
||||||
let mut buffer = Vec::<u8>::new();
|
|
||||||
stream.read_to_end(&mut buffer).unwrap();
|
|
||||||
|
|
||||||
bincode::deserialize(&buffer).unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_gpu_info(&self) -> GpuInfo {
|
|
||||||
let mut s = UnixStream::connect(SOCK_PATH).unwrap();
|
|
||||||
s.write_all(&bincode::serialize(&Action::GetInfo).unwrap())
|
|
||||||
.unwrap();
|
|
||||||
/*s.shutdown(std::net::Shutdown::Write)
|
|
||||||
.expect("Could not shut down");*/
|
|
||||||
|
|
||||||
let mut buffer = Vec::<u8>::new();
|
|
||||||
s.read_to_end(&mut buffer).unwrap();
|
|
||||||
|
|
||||||
bincode::deserialize(&buffer).unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn start_fan_control(&self) -> Result<(), DaemonConnectionError> {
|
|
||||||
let mut s = UnixStream::connect(SOCK_PATH).unwrap();
|
|
||||||
s.write_all(&bincode::serialize(&Action::StartFanControl).unwrap())
|
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
s.shutdown(std::net::Shutdown::Write).expect("Could not shut down");
|
||||||
|
|
||||||
let mut buffer = Vec::<u8>::new();
|
let mut buffer = Vec::<u8>::new();
|
||||||
s.read_to_end(&mut buffer).unwrap();
|
s.read_to_end(&mut buffer).unwrap();
|
||||||
|
|
||||||
|
bincode::deserialize(&buffer).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_gpu_info(&self, gpu_id: u32) -> GpuInfo {
|
||||||
|
let mut s = UnixStream::connect(SOCK_PATH).unwrap();
|
||||||
|
s.write_all(&bincode::serialize(&Action::GetInfo(gpu_id)).unwrap())
|
||||||
|
.unwrap();
|
||||||
|
s.shutdown(std::net::Shutdown::Write)
|
||||||
|
.expect("Could not shut down");
|
||||||
|
log::trace!("Sent action, receiving response");
|
||||||
|
|
||||||
|
let mut buffer = Vec::<u8>::new();
|
||||||
|
s.read_to_end(&mut buffer).unwrap();
|
||||||
|
log::trace!("Response recieved");
|
||||||
|
|
||||||
|
bincode::deserialize(&buffer).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn start_fan_control(&self, gpu_id: u32) -> Result<(), DaemonConnectionError> {
|
||||||
|
let mut s = UnixStream::connect(SOCK_PATH).unwrap();
|
||||||
|
s.write_all(&bincode::serialize(&Action::StartFanControl(gpu_id)).unwrap())
|
||||||
|
.unwrap();
|
||||||
|
s.shutdown(std::net::Shutdown::Write).expect("Could not shut down");
|
||||||
|
log::trace!("Sent action, receiving response");
|
||||||
|
|
||||||
|
let mut buffer = Vec::<u8>::new();
|
||||||
|
s.read_to_end(&mut buffer).unwrap();
|
||||||
|
log::trace!("Response recieved");
|
||||||
|
|
||||||
let result: Result<(), HWMonError> = bincode::deserialize(&buffer).unwrap();
|
let result: Result<(), HWMonError> = bincode::deserialize(&buffer).unwrap();
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
Ok(_) => Ok(()),
|
Ok(_) => Ok(()),
|
||||||
Err(_) => Err(DaemonConnectionError::PermissionDenied),
|
Err(_) => Err(DaemonConnectionError::PermissionDenied),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn stop_fan_control(&self) -> Result<(), DaemonConnectionError> {
|
pub fn stop_fan_control(&self, gpu_id: u32) -> Result<(), DaemonConnectionError> {
|
||||||
let mut s = UnixStream::connect(SOCK_PATH).unwrap();
|
let mut s = UnixStream::connect(SOCK_PATH).unwrap();
|
||||||
s.write_all(&bincode::serialize(&Action::StopFanControl).unwrap()).unwrap();
|
s.write_all(&bincode::serialize(&Action::StopFanControl(gpu_id)).unwrap())
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
s.shutdown(std::net::Shutdown::Write).expect("Could not shut down");
|
||||||
let mut buffer = Vec::<u8>::new();
|
let mut buffer = Vec::<u8>::new();
|
||||||
s.read_to_end(&mut buffer).unwrap();
|
s.read_to_end(&mut buffer).unwrap();
|
||||||
|
|
||||||
let result: Result<(), HWMonError> = bincode::deserialize(&buffer).unwrap();
|
let result: Result<(), HWMonError> = bincode::deserialize(&buffer).unwrap();
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
Ok(_) => Ok(()),
|
Ok(_) => Ok(()),
|
||||||
Err(_) => Err(DaemonConnectionError::PermissionDenied),
|
Err(_) => Err(DaemonConnectionError::PermissionDenied),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_fan_control(&self)-> FanControlInfo {
|
pub fn get_fan_control(&self, gpu_id: u32) -> FanControlInfo {
|
||||||
let mut s = UnixStream::connect(SOCK_PATH).unwrap();
|
let mut s = UnixStream::connect(SOCK_PATH).unwrap();
|
||||||
s.write_all(&bincode::serialize(&Action::GetFanControl).unwrap()).unwrap();
|
s.write_all(&bincode::serialize(&Action::GetFanControl(gpu_id)).unwrap())
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
s.shutdown(std::net::Shutdown::Write).expect("Could not shut down");
|
||||||
let mut buffer = Vec::<u8>::new();
|
let mut buffer = Vec::<u8>::new();
|
||||||
s.read_to_end(&mut buffer).unwrap();
|
s.read_to_end(&mut buffer).unwrap();
|
||||||
|
|
||||||
bincode::deserialize(&buffer).unwrap()
|
bincode::deserialize(&buffer).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_fan_curve(&self, curve: BTreeMap<i32, f64>) {
|
pub fn set_fan_curve(&self, gpu_id: u32, curve: BTreeMap<i32, f64>) {
|
||||||
let mut s = UnixStream::connect(SOCK_PATH).unwrap();
|
let mut s = UnixStream::connect(SOCK_PATH).unwrap();
|
||||||
s.write_all(&bincode::serialize(&Action::SetFanCurve).unwrap()).unwrap();
|
s.write_all(&bincode::serialize(&Action::SetFanCurve(gpu_id)).unwrap())
|
||||||
|
.unwrap();
|
||||||
|
s.shutdown(std::net::Shutdown::Write).expect("Could not shut down");
|
||||||
s.write_all(&bincode::serialize(&curve).unwrap()).unwrap();
|
s.write_all(&bincode::serialize(&curve).unwrap()).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_gpus(&self) -> HashMap<u32, String> {
|
||||||
|
log::trace!("sending request");
|
||||||
|
let mut s = UnixStream::connect(SOCK_PATH).unwrap();
|
||||||
|
s.write_all(&bincode::serialize(&Action::GetGpus).unwrap())
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
s.shutdown(std::net::Shutdown::Write).expect("Could not shut down");
|
||||||
|
|
||||||
|
log::trace!("sent request");
|
||||||
|
|
||||||
|
let mut buffer = Vec::<u8>::new();
|
||||||
|
s.read_to_end(&mut buffer).unwrap();
|
||||||
|
log::trace!("read response");
|
||||||
|
|
||||||
|
bincode::deserialize(&buffer).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn shutdown(&self) {
|
pub fn shutdown(&self) {
|
||||||
let mut s = UnixStream::connect(SOCK_PATH).unwrap();
|
let mut s = UnixStream::connect(SOCK_PATH).unwrap();
|
||||||
s.write_all(&bincode::serialize(&Action::Shutdown).unwrap()).unwrap();
|
s.write_all(&bincode::serialize(&Action::Shutdown).unwrap())
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
|
use crate::config::Config;
|
||||||
use crate::hw_mon::{HWMon, HWMonError};
|
use crate::hw_mon::{HWMon, HWMonError};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{collections::BTreeMap, fs};
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
use std::{collections::BTreeMap, fs};
|
||||||
use vulkano::instance::{Instance, InstanceExtensions, PhysicalDevice};
|
use vulkano::instance::{Instance, InstanceExtensions, PhysicalDevice};
|
||||||
use crate::config::Config;
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
pub struct GpuStats {
|
pub struct GpuStats {
|
||||||
@ -58,9 +58,18 @@ pub struct GpuInfo {
|
|||||||
|
|
||||||
impl GpuController {
|
impl GpuController {
|
||||||
pub fn new(hw_path: PathBuf, config: Config, config_path: PathBuf) -> Self {
|
pub fn new(hw_path: PathBuf, config: Config, config_path: PathBuf) -> Self {
|
||||||
let hwmon_path = fs::read_dir(&hw_path.join("hwmon")).unwrap().next().unwrap().unwrap().path();
|
let hwmon_path = fs::read_dir(&hw_path.join("hwmon"))
|
||||||
|
.unwrap()
|
||||||
|
.next()
|
||||||
|
.unwrap()
|
||||||
|
.unwrap()
|
||||||
|
.path();
|
||||||
|
|
||||||
let hw_mon = HWMon::new(&hwmon_path, config.fan_control_enabled, config.fan_curve.clone());
|
let hw_mon = HWMon::new(
|
||||||
|
&hwmon_path,
|
||||||
|
config.fan_control_enabled,
|
||||||
|
config.fan_curve.clone(),
|
||||||
|
);
|
||||||
|
|
||||||
let mut controller = GpuController {
|
let mut controller = GpuController {
|
||||||
hw_path: hw_path.clone(),
|
hw_path: hw_path.clone(),
|
||||||
@ -118,7 +127,12 @@ impl GpuController {
|
|||||||
card_model_id.to_lowercase()
|
card_model_id.to_lowercase()
|
||||||
);
|
);
|
||||||
|
|
||||||
for line in full_hwid_list.split('\n') {
|
let lines: Vec<&str> = full_hwid_list.split('\n').collect();
|
||||||
|
|
||||||
|
//for line in full_hwid_list.split('\n') {
|
||||||
|
for i in 0..lines.len() {
|
||||||
|
let line = lines[i];
|
||||||
|
|
||||||
if line.len() > card_vendor_id.len() {
|
if line.len() > card_vendor_id.len() {
|
||||||
if line[0..card_vendor_id.len()] == card_vendor_id.to_lowercase() {
|
if line[0..card_vendor_id.len()] == card_vendor_id.to_lowercase() {
|
||||||
card_vendor = line.splitn(2, ' ').collect::<Vec<&str>>()[1]
|
card_vendor = line.splitn(2, ' ').collect::<Vec<&str>>()[1]
|
||||||
@ -138,8 +152,8 @@ impl GpuController {
|
|||||||
Ok(v) => v,
|
Ok(v) => v,
|
||||||
Err(_) => "".to_string(),
|
Err(_) => "".to_string(),
|
||||||
}
|
}
|
||||||
.trim()
|
.trim()
|
||||||
.to_string();
|
.to_string();
|
||||||
|
|
||||||
let vram_size = match fs::read_to_string(self.hw_path.join("mem_info_vram_total")) {
|
let vram_size = match fs::read_to_string(self.hw_path.join("mem_info_vram_total")) {
|
||||||
Ok(a) => a.trim().parse::<u64>().unwrap() / 1024 / 1024,
|
Ok(a) => a.trim().parse::<u64>().unwrap() / 1024 / 1024,
|
||||||
@ -156,7 +170,6 @@ impl GpuController {
|
|||||||
Err(_) => 0,
|
Err(_) => 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
let vulkan_info = GpuController::get_vulkan_info(&model_id);
|
let vulkan_info = GpuController::get_vulkan_info(&model_id);
|
||||||
let max_fan_speed = self.hw_mon.fan_max_speed;
|
let max_fan_speed = self.hw_mon.fan_max_speed;
|
||||||
|
|
||||||
@ -214,9 +227,11 @@ impl GpuController {
|
|||||||
match self.hw_mon.start_fan_control() {
|
match self.hw_mon.start_fan_control() {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
self.config.fan_control_enabled = true;
|
self.config.fan_control_enabled = true;
|
||||||
self.config.save(&self.config_path).expect("Failed to save config");
|
self.config
|
||||||
|
.save(&self.config_path)
|
||||||
|
.expect("Failed to save config");
|
||||||
Ok(())
|
Ok(())
|
||||||
},
|
}
|
||||||
Err(e) => Err(e),
|
Err(e) => Err(e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -225,9 +240,11 @@ impl GpuController {
|
|||||||
match self.hw_mon.stop_fan_control() {
|
match self.hw_mon.stop_fan_control() {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
self.config.fan_control_enabled = false;
|
self.config.fan_control_enabled = false;
|
||||||
self.config.save(&self.config_path).expect("Failed to save config");
|
self.config
|
||||||
|
.save(&self.config_path)
|
||||||
|
.expect("Failed to save config");
|
||||||
Ok(())
|
Ok(())
|
||||||
},
|
}
|
||||||
Err(e) => Err(e),
|
Err(e) => Err(e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -244,7 +261,9 @@ impl GpuController {
|
|||||||
pub fn set_fan_curve(&mut self, curve: BTreeMap<i32, f64>) {
|
pub fn set_fan_curve(&mut self, curve: BTreeMap<i32, f64>) {
|
||||||
self.hw_mon.set_fan_curve(curve.clone());
|
self.hw_mon.set_fan_curve(curve.clone());
|
||||||
self.config.fan_curve = curve;
|
self.config.fan_curve = curve;
|
||||||
self.config.save(&self.config_path).expect("Failed to save config");
|
self.config
|
||||||
|
.save(&self.config_path)
|
||||||
|
.expect("Failed to save config");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_vulkan_info(pci_id: &str) -> VulkanInfo {
|
fn get_vulkan_info(pci_id: &str) -> VulkanInfo {
|
||||||
|
@ -26,12 +26,15 @@ pub struct HWMon {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl HWMon {
|
impl HWMon {
|
||||||
pub fn new(hwmon_path: &PathBuf, fan_control_enabled: bool, fan_curve: BTreeMap<i32, f64>) -> HWMon {
|
pub fn new(
|
||||||
let fan_max_speed = fs::read_to_string(hwmon_path.join("fan1_max"))
|
hwmon_path: &PathBuf,
|
||||||
.unwrap()
|
fan_control_enabled: bool,
|
||||||
.trim()
|
fan_curve: BTreeMap<i32, f64>,
|
||||||
.parse::<i32>()
|
) -> HWMon {
|
||||||
.unwrap();
|
let fan_max_speed = match fs::read_to_string(hwmon_path.join("fan1_max")) {
|
||||||
|
Ok(s) => s.trim().parse::<i32>().unwrap(),
|
||||||
|
Err(_) => 0,
|
||||||
|
};
|
||||||
|
|
||||||
let mon = HWMon {
|
let mon = HWMon {
|
||||||
hwmon_path: hwmon_path.clone(),
|
hwmon_path: hwmon_path.clone(),
|
||||||
@ -43,7 +46,7 @@ impl HWMon {
|
|||||||
if fan_control_enabled {
|
if fan_control_enabled {
|
||||||
mon.start_fan_control().unwrap();
|
mon.start_fan_control().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
mon
|
mon
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,38 +1,44 @@
|
|||||||
|
pub mod config;
|
||||||
|
pub mod daemon_connection;
|
||||||
pub mod gpu_controller;
|
pub mod gpu_controller;
|
||||||
pub mod hw_mon;
|
pub mod hw_mon;
|
||||||
pub mod daemon_connection;
|
|
||||||
pub mod config;
|
|
||||||
|
|
||||||
use std::{io::{Read, Write}, path::PathBuf, thread};
|
|
||||||
use config::Config;
|
use config::Config;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::fs;
|
use std::{collections::HashMap, fs};
|
||||||
use std::os::unix::net::{UnixListener, UnixStream};
|
use std::os::unix::net::{UnixListener, UnixStream};
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
use std::{
|
||||||
|
io::{Read, Write},
|
||||||
|
path::PathBuf,
|
||||||
|
thread,
|
||||||
|
};
|
||||||
|
use rand::prelude::*;
|
||||||
|
|
||||||
use crate::gpu_controller::GpuController;
|
use crate::gpu_controller::GpuController;
|
||||||
|
|
||||||
pub const SOCK_PATH: &str = "/tmp/amdgpu-configurator.sock";
|
pub const SOCK_PATH: &str = "/tmp/amdgpu-configurator.sock";
|
||||||
|
|
||||||
pub struct Daemon {
|
pub struct Daemon {
|
||||||
gpu_controller: GpuController,
|
gpu_controllers: HashMap<u32, GpuController>,
|
||||||
listener: UnixListener,
|
listener: UnixListener,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
pub enum Action {
|
pub enum Action {
|
||||||
CheckAlive,
|
CheckAlive,
|
||||||
GetInfo,
|
GetGpus,
|
||||||
GetStats,
|
GetInfo(u32),
|
||||||
StartFanControl,
|
GetStats(u32),
|
||||||
StopFanControl,
|
StartFanControl(u32),
|
||||||
GetFanControl,
|
StopFanControl(u32),
|
||||||
SetFanCurve,
|
GetFanControl(u32),
|
||||||
|
SetFanCurve(u32),
|
||||||
Shutdown,
|
Shutdown,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Daemon {
|
impl Daemon {
|
||||||
pub fn new() -> Daemon {
|
pub fn new(unpriveleged: bool) -> Daemon {
|
||||||
if fs::metadata(SOCK_PATH).is_ok() {
|
if fs::metadata(SOCK_PATH).is_ok() {
|
||||||
fs::remove_file(SOCK_PATH).expect("Failed to take control over socket");
|
fs::remove_file(SOCK_PATH).expect("Failed to take control over socket");
|
||||||
}
|
}
|
||||||
@ -46,19 +52,43 @@ impl Daemon {
|
|||||||
.expect("Failed to chmod");
|
.expect("Failed to chmod");
|
||||||
|
|
||||||
let config_path = PathBuf::from("/etc/lact.json");
|
let config_path = PathBuf::from("/etc/lact.json");
|
||||||
let config = match Config::read_from_file(&config_path) {
|
let config = if unpriveleged {
|
||||||
Ok(c) => c,
|
Config::new()
|
||||||
Err(_) => {
|
} else {
|
||||||
let c = Config::new();
|
match Config::read_from_file(&config_path) {
|
||||||
c.save(&config_path).expect("Failed to save config");
|
Ok(c) => c,
|
||||||
c
|
Err(_) => {
|
||||||
|
let c = Config::new();
|
||||||
|
c.save(&config_path).expect("Failed to save config");
|
||||||
|
c
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
log::trace!("Using config {:?}", config);
|
log::trace!("Using config {:?}", config);
|
||||||
|
|
||||||
let gpu_controller = GpuController::new(PathBuf::from("/sys/class/drm/card0/device"), config, config_path);
|
let mut gpu_controllers: HashMap<u32, GpuController> = HashMap::new();
|
||||||
|
|
||||||
Daemon { listener, gpu_controller }
|
for entry in fs::read_dir("/sys/class/drm").expect("Could not open /sys/class/drm") {
|
||||||
|
let entry = entry.unwrap();
|
||||||
|
if entry.file_name().len() == 5 {
|
||||||
|
if entry.file_name().to_str().unwrap().split_at(4).0 == "card" {
|
||||||
|
log::info!("Initializing {:?}", entry.path());
|
||||||
|
loop {
|
||||||
|
let id: u32 = random();
|
||||||
|
if !gpu_controllers.contains_key(&id) {
|
||||||
|
gpu_controllers.insert(id, GpuController::new(entry.path().join("device"), config.clone(), config_path.clone()));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Daemon {
|
||||||
|
listener,
|
||||||
|
gpu_controllers,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn listen(mut self) {
|
pub fn listen(mut self) {
|
||||||
@ -67,7 +97,7 @@ impl Daemon {
|
|||||||
Ok(stream) => {
|
Ok(stream) => {
|
||||||
//let mut controller = self.gpu_controller.clone();
|
//let mut controller = self.gpu_controller.clone();
|
||||||
//thread::spawn(move || Daemon::handle_connection(&mut controller, stream));
|
//thread::spawn(move || Daemon::handle_connection(&mut controller, stream));
|
||||||
Daemon::handle_connection(&mut self.gpu_controller, stream);
|
Daemon::handle_connection(&mut self.gpu_controllers, stream);
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
log::error!("Error: {}", err);
|
log::error!("Error: {}", err);
|
||||||
@ -77,41 +107,68 @@ impl Daemon {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_connection(gpu_controller: &mut GpuController, mut stream: UnixStream) {
|
fn handle_connection(gpu_controllers: &mut HashMap<u32, GpuController>, mut stream: UnixStream) {
|
||||||
let mut buffer: [u8; 4] = [0; 4];
|
log::trace!("Reading buffer");
|
||||||
stream.read(&mut buffer).unwrap();
|
let mut buffer = Vec::<u8>::new();
|
||||||
|
stream.read_to_end(&mut buffer).unwrap();
|
||||||
//log::trace!("finished reading, buffer size {}", buffer.len());
|
//log::trace!("finished reading, buffer size {}", buffer.len());
|
||||||
|
log::trace!("Attempting to deserialize {:?}", &buffer);
|
||||||
let action: Action = bincode::deserialize(&buffer).expect("Failed to deserialize buffer");
|
let action: Action = bincode::deserialize(&buffer).expect("Failed to deserialize buffer");
|
||||||
//log::trace!("{:?}", action);
|
//log::trace!("{:?}", action);
|
||||||
|
|
||||||
|
log::trace!("Executing action {:?}", action);
|
||||||
let response: Option<Vec<u8>> = match action {
|
let response: Option<Vec<u8>> = match action {
|
||||||
Action::GetStats => Some(bincode::serialize(&gpu_controller.get_stats()).unwrap()),
|
Action::CheckAlive => Some(vec![1]),
|
||||||
Action::GetInfo => Some(bincode::serialize(&gpu_controller.gpu_info).unwrap()),
|
Action::GetGpus => {
|
||||||
Action::StartFanControl => Some(bincode::serialize(&gpu_controller.start_fan_control()).unwrap()),
|
let mut gpus: HashMap<u32, String> = HashMap::new();
|
||||||
Action::StopFanControl => Some(bincode::serialize(&gpu_controller.stop_fan_control()).unwrap()),
|
for controller in gpu_controllers {
|
||||||
Action::GetFanControl => Some(bincode::serialize(&gpu_controller.get_fan_control()).unwrap()),
|
gpus.insert(*controller.0, controller.1.gpu_info.gpu_model.clone());
|
||||||
Action::SetFanCurve => {
|
}
|
||||||
|
Some(bincode::serialize(&gpus).unwrap())
|
||||||
|
},
|
||||||
|
Action::GetStats(i) => match gpu_controllers.get(&i) {
|
||||||
|
Some(controller) => Some(bincode::serialize(&controller.get_stats()).unwrap()),
|
||||||
|
_ => None,
|
||||||
|
},
|
||||||
|
Action::GetInfo(i) => match gpu_controllers.get(&i) {
|
||||||
|
Some(controller) => Some(bincode::serialize(&controller.gpu_info).unwrap()),
|
||||||
|
_ => None,
|
||||||
|
},
|
||||||
|
Action::StartFanControl(i) => match gpu_controllers.get_mut(&i) {
|
||||||
|
Some(controller) => Some(bincode::serialize(&controller.start_fan_control()).unwrap()),
|
||||||
|
_ => None,
|
||||||
|
},
|
||||||
|
Action::StopFanControl(i) => match gpu_controllers.get_mut(&i) {
|
||||||
|
Some(controller) => Some(bincode::serialize(&controller.stop_fan_control()).unwrap()),
|
||||||
|
_ => None,
|
||||||
|
},
|
||||||
|
Action::GetFanControl(i) => match gpu_controllers.get(&i) {
|
||||||
|
Some(controller) => Some(bincode::serialize(&controller.get_fan_control()).unwrap()),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
Action::SetFanCurve(i) => {
|
||||||
let mut buffer = Vec::new();
|
let mut buffer = Vec::new();
|
||||||
stream.read_to_end(&mut buffer).unwrap();
|
stream.read_to_end(&mut buffer).unwrap();
|
||||||
gpu_controller.set_fan_curve(bincode::deserialize(&buffer).expect("Failed to deserialize curve"));
|
gpu_controllers.get_mut(&i).unwrap().set_fan_curve(
|
||||||
|
bincode::deserialize(&buffer).expect("Failed to deserialize curve"),
|
||||||
|
);
|
||||||
None
|
None
|
||||||
},
|
}
|
||||||
Action::CheckAlive => Some(vec![1]),
|
|
||||||
Action::Shutdown => std::process::exit(0),
|
Action::Shutdown => std::process::exit(0),
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(r) = &response {
|
if let Some(r) = &response {
|
||||||
stream
|
log::trace!("Responding");
|
||||||
.write_all(&r)
|
stream.write_all(&r).expect("Failed writing response");
|
||||||
.expect("Failed writing response");
|
//stream
|
||||||
|
// .shutdown(std::net::Shutdown::Write)
|
||||||
|
// .expect("Could not shut down");
|
||||||
|
log::trace!("Finished responding");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum DaemonError {
|
pub enum DaemonError {
|
||||||
ConnectionFailed,
|
ConnectionFailed,
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,6 @@ use daemon::Daemon;
|
|||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
env_logger::init();
|
env_logger::init();
|
||||||
let d = Daemon::new();
|
let d = Daemon::new(false);
|
||||||
d.listen();
|
d.listen();
|
||||||
}
|
}
|
||||||
|
@ -2,13 +2,22 @@ extern crate gdk;
|
|||||||
extern crate gio;
|
extern crate gio;
|
||||||
extern crate gtk;
|
extern crate gtk;
|
||||||
|
|
||||||
use daemon::{Daemon, daemon_connection::DaemonConnection};
|
use daemon::{daemon_connection::DaemonConnection, Daemon};
|
||||||
use gio::prelude::*;
|
use gio::prelude::*;
|
||||||
use gtk::{Adjustment, Button, ButtonsType, DialogFlags, Frame, Label, LevelBar, MessageType, Switch, prelude::*};
|
use gtk::{
|
||||||
|
prelude::*, Adjustment, Button, ButtonsType, DialogFlags, Frame, Label, LevelBar, MessageType,
|
||||||
|
Switch,
|
||||||
|
};
|
||||||
|
|
||||||
use gtk::{Builder, MessageDialog, TextBuffer, Window};
|
use gtk::{Builder, MessageDialog, TextBuffer, Window};
|
||||||
|
|
||||||
use std::{collections::BTreeMap, env::args, sync::{Arc, RwLock}, thread, time::Duration};
|
use std::{
|
||||||
|
collections::BTreeMap,
|
||||||
|
env::args,
|
||||||
|
sync::{Arc, RwLock},
|
||||||
|
thread,
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
|
|
||||||
fn build_ui(application: >k::Application) {
|
fn build_ui(application: >k::Application) {
|
||||||
let glade_src = include_str!("main_window.glade");
|
let glade_src = include_str!("main_window.glade");
|
||||||
@ -62,31 +71,23 @@ fn build_ui(application: >k::Application) {
|
|||||||
let vram_usage_label: Label = builder
|
let vram_usage_label: Label = builder
|
||||||
.get_object("vram_usage_label")
|
.get_object("vram_usage_label")
|
||||||
.expect("Couldn't get label");
|
.expect("Couldn't get label");
|
||||||
|
|
||||||
let gpu_clock_text_buffer: TextBuffer = builder
|
|
||||||
.get_object("gpu_clock_text_buffer").unwrap();
|
|
||||||
|
|
||||||
let vram_clock_text_buffer: TextBuffer = builder
|
let gpu_clock_text_buffer: TextBuffer = builder.get_object("gpu_clock_text_buffer").unwrap();
|
||||||
.get_object("vram_clock_text_buffer").unwrap();
|
|
||||||
|
|
||||||
let gpu_temp_text_buffer: TextBuffer = builder
|
let vram_clock_text_buffer: TextBuffer = builder.get_object("vram_clock_text_buffer").unwrap();
|
||||||
.get_object("gpu_temp_text_buffer").unwrap();
|
|
||||||
|
|
||||||
let gpu_power_text_buffer: TextBuffer = builder
|
let gpu_temp_text_buffer: TextBuffer = builder.get_object("gpu_temp_text_buffer").unwrap();
|
||||||
.get_object("gpu_power_text_buffer").unwrap();
|
|
||||||
|
|
||||||
let fan_speed_text_buffer: TextBuffer = builder
|
let gpu_power_text_buffer: TextBuffer = builder.get_object("gpu_power_text_buffer").unwrap();
|
||||||
.get_object("fan_speed_text_buffer").unwrap();
|
|
||||||
|
|
||||||
let automatic_fan_control_switch: Switch = builder
|
let fan_speed_text_buffer: TextBuffer = builder.get_object("fan_speed_text_buffer").unwrap();
|
||||||
.get_object("automatic_fan_control_switch").unwrap();
|
|
||||||
|
|
||||||
let apply_button: Button = builder
|
let automatic_fan_control_switch: Switch =
|
||||||
.get_object("apply_button").unwrap();
|
builder.get_object("automatic_fan_control_switch").unwrap();
|
||||||
|
|
||||||
let fan_curve_frame: Frame = builder
|
let apply_button: Button = builder.get_object("apply_button").unwrap();
|
||||||
.get_object("fan_curve_frame").unwrap();
|
|
||||||
|
|
||||||
|
let fan_curve_frame: Frame = builder.get_object("fan_curve_frame").unwrap();
|
||||||
|
|
||||||
let mut unpriviliged: bool = false;
|
let mut unpriviliged: bool = false;
|
||||||
|
|
||||||
@ -95,7 +96,7 @@ fn build_ui(application: >k::Application) {
|
|||||||
Err(_) => {
|
Err(_) => {
|
||||||
unpriviliged = true;
|
unpriviliged = true;
|
||||||
|
|
||||||
let daemon = Daemon::new();
|
let daemon = Daemon::new(unpriviliged);
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
daemon.listen();
|
daemon.listen();
|
||||||
});
|
});
|
||||||
@ -139,20 +140,17 @@ fn build_ui(application: >k::Application) {
|
|||||||
|
|
||||||
let (tx, rx) = glib::MainContext::channel(glib::PRIORITY_DEFAULT);
|
let (tx, rx) = glib::MainContext::channel(glib::PRIORITY_DEFAULT);
|
||||||
|
|
||||||
thread::spawn(move || {
|
thread::spawn(move || loop {
|
||||||
loop {
|
let gpu_stats = d.get_gpu_stats();
|
||||||
let gpu_stats = d.get_gpu_stats();
|
|
||||||
|
|
||||||
tx.send(gpu_stats).expect("Couldn't send text");
|
tx.send(gpu_stats).expect("Couldn't send text");
|
||||||
thread::sleep(Duration::from_millis(500));
|
thread::sleep(Duration::from_millis(500));
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
rx.attach(None, move |gpu_stats| {
|
rx.attach(None, move |gpu_stats| {
|
||||||
vram_usage_level_bar.set_max_value(gpu_stats.mem_total as f64);
|
vram_usage_level_bar.set_max_value(gpu_stats.mem_total as f64);
|
||||||
vram_usage_level_bar.set_value(gpu_stats.mem_used as f64);
|
vram_usage_level_bar.set_value(gpu_stats.mem_used as f64);
|
||||||
|
|
||||||
let text = format!("{}/{} MiB", gpu_stats.mem_used, gpu_stats.mem_total);
|
let text = format!("{}/{} MiB", gpu_stats.mem_used, gpu_stats.mem_total);
|
||||||
vram_usage_label.set_text(&text);
|
vram_usage_label.set_text(&text);
|
||||||
|
|
||||||
@ -161,9 +159,14 @@ fn build_ui(application: >k::Application) {
|
|||||||
|
|
||||||
gpu_temp_text_buffer.set_text(&format!("{}°C", gpu_stats.gpu_temp));
|
gpu_temp_text_buffer.set_text(&format!("{}°C", gpu_stats.gpu_temp));
|
||||||
|
|
||||||
gpu_power_text_buffer.set_text(&format!("{}/{}W", gpu_stats.power_avg, gpu_stats.power_max));
|
gpu_power_text_buffer
|
||||||
|
.set_text(&format!("{}/{}W", gpu_stats.power_avg, gpu_stats.power_max));
|
||||||
|
|
||||||
fan_speed_text_buffer.set_text(&format!("{}RPM({}%)", gpu_stats.fan_speed, (gpu_stats.fan_speed as f64 / gpu_info.max_fan_speed as f64 * 100 as f64) as i32));
|
fan_speed_text_buffer.set_text(&format!(
|
||||||
|
"{}RPM({}%)",
|
||||||
|
gpu_stats.fan_speed,
|
||||||
|
(gpu_stats.fan_speed as f64 / gpu_info.max_fan_speed as f64 * 100 as f64) as i32
|
||||||
|
));
|
||||||
|
|
||||||
glib::Continue(true)
|
glib::Continue(true)
|
||||||
});
|
});
|
||||||
@ -174,8 +177,7 @@ fn build_ui(application: >k::Application) {
|
|||||||
println!("Automatic fan control disabled!");
|
println!("Automatic fan control disabled!");
|
||||||
automatic_fan_control_switch.set_active(false);
|
automatic_fan_control_switch.set_active(false);
|
||||||
fan_curve_frame.set_visible(true);
|
fan_curve_frame.set_visible(true);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
println!("Automatic fan control enabled");
|
println!("Automatic fan control enabled");
|
||||||
fan_curve_frame.set_visible(false);
|
fan_curve_frame.set_visible(false);
|
||||||
}
|
}
|
||||||
@ -187,8 +189,8 @@ fn build_ui(application: >k::Application) {
|
|||||||
match switch.get_active() {
|
match switch.get_active() {
|
||||||
true => {
|
true => {
|
||||||
fan_curve_frame.set_visible(false);
|
fan_curve_frame.set_visible(false);
|
||||||
},
|
}
|
||||||
false => {
|
false => {
|
||||||
fan_curve_frame.set_visible(true);
|
fan_curve_frame.set_visible(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -200,20 +202,24 @@ fn build_ui(application: >k::Application) {
|
|||||||
|
|
||||||
for i in 1..6 {
|
for i in 1..6 {
|
||||||
let curve_temperature_adjustment: Adjustment = builder
|
let curve_temperature_adjustment: Adjustment = builder
|
||||||
.get_object(&format!("curve_temperature_adjustment_{}", i)).unwrap();
|
.get_object(&format!("curve_temperature_adjustment_{}", i))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let value = *curve.read().unwrap().get(&(i * 20)).expect("Could not get by index");
|
let value = *curve
|
||||||
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.get(&(i * 20))
|
||||||
|
.expect("Could not get by index");
|
||||||
println!("Setting value {} on adjustment {}", value, i);
|
println!("Setting value {} on adjustment {}", value, i);
|
||||||
curve_temperature_adjustment.set_value(value);
|
curve_temperature_adjustment.set_value(value);
|
||||||
|
|
||||||
let c = curve.clone();
|
let c = curve.clone();
|
||||||
let b = apply_button.clone();
|
let b = apply_button.clone();
|
||||||
|
|
||||||
curve_temperature_adjustment.connect_value_changed(move |adj | {
|
curve_temperature_adjustment.connect_value_changed(move |adj| {
|
||||||
c.write().unwrap().insert(20 * i, adj.get_value());
|
c.write().unwrap().insert(20 * i, adj.get_value());
|
||||||
b.set_sensitive(true);
|
b.set_sensitive(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
apply_button.connect_clicked(move |b| {
|
apply_button.connect_clicked(move |b| {
|
||||||
@ -225,8 +231,8 @@ fn build_ui(application: >k::Application) {
|
|||||||
match automatic_fan_control_switch.get_active() {
|
match automatic_fan_control_switch.get_active() {
|
||||||
true => {
|
true => {
|
||||||
d.stop_fan_control().unwrap();
|
d.stop_fan_control().unwrap();
|
||||||
},
|
}
|
||||||
false => {
|
false => {
|
||||||
d.start_fan_control().unwrap();
|
d.start_fan_control().unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -237,7 +243,6 @@ fn build_ui(application: >k::Application) {
|
|||||||
main_window.show();
|
main_window.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
println!("Initializing gtk");
|
println!("Initializing gtk");
|
||||||
let application = gtk::Application::new(Some("com.ilyaz.yagc"), Default::default())
|
let application = gtk::Application::new(Some("com.ilyaz.yagc"), Default::default())
|
||||||
|
Loading…
Reference in New Issue
Block a user