mirror of
https://github.com/ilya-zlobintsev/LACT.git
synced 2025-02-25 18:55:26 -06:00
Added fan control and curve support
This commit is contained in:
@@ -7,7 +7,9 @@ enum Opt {
|
||||
///Gets realtime GPU information
|
||||
Stats,
|
||||
Info,
|
||||
Start,
|
||||
StartFanControl,
|
||||
StopFanControl,
|
||||
GetFanControl,
|
||||
Stop,
|
||||
}
|
||||
|
||||
@@ -29,11 +31,15 @@ fn main() {
|
||||
println!("Driver in use: {}", gpu_info.driver);
|
||||
print!("VBIOS Version: {}", gpu_info.vbios_version);
|
||||
},
|
||||
Opt::Start => {
|
||||
Opt::StartFanControl => {
|
||||
println!("{:?}", d.start_fan_control());
|
||||
},
|
||||
Opt::Stop => {
|
||||
Opt::StopFanControl => {
|
||||
println!("{:?}", d.stop_fan_control());
|
||||
}
|
||||
},
|
||||
Opt::GetFanControl => {
|
||||
println!("{:?}", d.get_fan_control());
|
||||
},
|
||||
Opt::Stop => d.shutdown(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::{Action, SOCK_PATH, gpu_controller::GpuInfo, gpu_controller::GpuStats, hw_mon::HWMonError};
|
||||
use std::io::{Read, Write};
|
||||
use crate::{Action, SOCK_PATH, gpu_controller::GpuInfo, gpu_controller::{FanControlInfo, GpuStats}, hw_mon::HWMonError};
|
||||
use std::{collections::BTreeMap, io::{Read, Write}};
|
||||
use std::os::unix::net::UnixStream;
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -89,4 +89,25 @@ impl DaemonConnection {
|
||||
Err(_) => Err(DaemonConnectionError::PermissionDenied),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_fan_control(&self)-> FanControlInfo {
|
||||
let mut s = UnixStream::connect(SOCK_PATH).unwrap();
|
||||
s.write_all(&bincode::serialize(&Action::GetFanControl).unwrap()).unwrap();
|
||||
|
||||
let mut buffer = Vec::<u8>::new();
|
||||
s.read_to_end(&mut buffer).unwrap();
|
||||
|
||||
bincode::deserialize(&buffer).unwrap()
|
||||
}
|
||||
|
||||
pub fn set_fan_curve(&self, curve: BTreeMap<i32, f64>) {
|
||||
let mut s = UnixStream::connect(SOCK_PATH).unwrap();
|
||||
s.write_all(&bincode::serialize(&Action::SetFanCurve).unwrap()).unwrap();
|
||||
s.write_all(&bincode::serialize(&curve).unwrap()).unwrap();
|
||||
}
|
||||
|
||||
pub fn shutdown(&self) {
|
||||
let mut s = UnixStream::connect(SOCK_PATH).unwrap();
|
||||
s.write_all(&bincode::serialize(&Action::Shutdown).unwrap()).unwrap();
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::hw_mon::{HWMon, HWMonError};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fs;
|
||||
use std::{collections::BTreeMap, fs};
|
||||
use std::path::PathBuf;
|
||||
use vulkano::instance::{Instance, InstanceExtensions, PhysicalDevice};
|
||||
|
||||
@@ -16,6 +16,12 @@ pub struct GpuStats {
|
||||
pub fan_speed: i32,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct FanControlInfo {
|
||||
pub enabled: bool,
|
||||
pub curve: BTreeMap<i32, f64>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct GpuController {
|
||||
hw_path: PathBuf,
|
||||
@@ -48,9 +54,11 @@ pub struct GpuInfo {
|
||||
|
||||
impl GpuController {
|
||||
pub fn new(hw_path: PathBuf) -> Self {
|
||||
let hwmon_path = fs::read_dir(&hw_path.join("hwmon")).unwrap().next().unwrap().unwrap().path();
|
||||
|
||||
let mut controller = GpuController {
|
||||
hw_path: hw_path.clone(),
|
||||
hw_mon: HWMon::new(&hw_path.join("hwmon/hwmon0")),
|
||||
hw_mon: HWMon::new(&hwmon_path),
|
||||
gpu_info: Default::default(),
|
||||
};
|
||||
controller.gpu_info = controller.get_info();
|
||||
@@ -200,6 +208,19 @@ impl GpuController {
|
||||
self.hw_mon.stop_fan_control()
|
||||
}
|
||||
|
||||
pub fn get_fan_control(&self) -> FanControlInfo {
|
||||
let control = self.hw_mon.get_fan_control();
|
||||
|
||||
FanControlInfo {
|
||||
enabled: control.0,
|
||||
curve: control.1,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_fan_curve(&self, curve: BTreeMap<i32, f64>) {
|
||||
self.hw_mon.set_fan_curve(curve);
|
||||
}
|
||||
|
||||
fn get_vulkan_info(pci_id: &str) -> VulkanInfo {
|
||||
let instance = Instance::new(None, &InstanceExtensions::none(), None)
|
||||
.expect("failed to create instance");
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use std:: {fs, path::PathBuf, sync::{Arc, atomic::{AtomicBool, Ordering}}, thread, time::Duration};
|
||||
use std:: {collections::{BTreeMap}, fs, path::PathBuf, sync::{Arc, RwLock, atomic::{AtomicBool, Ordering}}, thread, time::Duration};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@@ -7,21 +7,29 @@ pub enum HWMonError {
|
||||
PermissionDenied,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct HWMon {
|
||||
hwmon_path: PathBuf,
|
||||
fan_max_speed: i32,
|
||||
fan_control: Arc<AtomicBool>,
|
||||
fan_curve: Arc<RwLock<BTreeMap<i32, f64>>>,
|
||||
}
|
||||
|
||||
impl HWMon {
|
||||
pub fn new(hwmon_path: &PathBuf) -> HWMon {
|
||||
let fan_max_speed = fs::read_to_string(hwmon_path.join("fan1_max")).unwrap().trim().parse::<i32>().unwrap();
|
||||
let mut fan_curve: BTreeMap<i32, f64> = BTreeMap::new();
|
||||
fan_curve.insert(20, 0f64);
|
||||
fan_curve.insert(40, 0f64);
|
||||
fan_curve.insert(60, 50f64);
|
||||
fan_curve.insert(80, 80f64);
|
||||
fan_curve.insert(100, 100f64);
|
||||
|
||||
HWMon {
|
||||
hwmon_path: hwmon_path.clone(),
|
||||
fan_max_speed,
|
||||
fan_control: Arc::new(AtomicBool::new(false)),
|
||||
fan_curve: Arc::new(RwLock::new(fan_curve)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,7 +71,21 @@ impl HWMon {
|
||||
fs::read_to_string(filename).unwrap().trim().parse::<i32>().unwrap() / 1000000
|
||||
}
|
||||
|
||||
pub fn set_fan_curve(&self, curve: BTreeMap<i32, f64>) {
|
||||
println!("trying to set curve");
|
||||
let mut current = self.fan_curve.write().unwrap();
|
||||
current.clear();
|
||||
|
||||
for (k, v) in curve.iter() {
|
||||
current.insert(k.clone(), v.clone());
|
||||
}
|
||||
println!("set curve to {:?}", current);
|
||||
}
|
||||
|
||||
pub fn start_fan_control(&self) -> Result<(), HWMonError> {
|
||||
if self.fan_control.load(Ordering::SeqCst) {
|
||||
return Ok(());
|
||||
}
|
||||
self.fan_control.store(true, Ordering::SeqCst);
|
||||
|
||||
match fs::write(self.hwmon_path.join("pwm1_enable"), "1") {
|
||||
@@ -72,8 +94,30 @@ impl HWMon {
|
||||
|
||||
thread::spawn(move || {
|
||||
while s.fan_control.load(Ordering::SeqCst) {
|
||||
let curve = s.fan_curve.read().unwrap();
|
||||
|
||||
let temp = s.get_gpu_temp();
|
||||
println!("{}", temp);
|
||||
println!("Current gpu temp: {}", temp);
|
||||
for (t_low, s_low) in curve.iter() {
|
||||
match curve.range(t_low..).nth(1) {
|
||||
Some((t_high, s_high)) => {
|
||||
if (t_low..t_high).contains(&&temp) {
|
||||
let speed_ratio = (temp - t_low) as f64 / (t_high - t_low) as f64; //The ratio of which speed to choose within the range of current lower and upper speeds
|
||||
let speed_percent = s_low + ((s_high - s_low) * speed_ratio);
|
||||
let pwm = (255f64 * (speed_percent / 100f64)) as i32;
|
||||
println!("pwm: {}", pwm);
|
||||
|
||||
fs::write(s.hwmon_path.join("pwm1"), pwm.to_string()).expect("Failed to write to pwm1");
|
||||
|
||||
println!("In the range of {}..{}c {}..{}%, setting speed {}% ratio {}", t_low, t_high, s_low, s_high, speed_percent, speed_ratio);
|
||||
break;
|
||||
}
|
||||
}
|
||||
None => (),
|
||||
}
|
||||
}
|
||||
drop(curve); //needed to release rwlock so that the curve can be changed
|
||||
|
||||
thread::sleep(Duration::from_millis(1000));
|
||||
}
|
||||
});
|
||||
@@ -92,6 +136,10 @@ impl HWMon {
|
||||
},
|
||||
Err(_) => Err(HWMonError::PermissionDenied)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
pub fn get_fan_control(&self) -> (bool, BTreeMap<i32, f64>) {
|
||||
println!("Fan control: {}", self.fan_control.load(Ordering::SeqCst));
|
||||
(self.fan_control.load(Ordering::SeqCst), self.fan_curve.read().unwrap().clone())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ pub mod gpu_controller;
|
||||
pub mod hw_mon;
|
||||
pub mod daemon_connection;
|
||||
|
||||
use std::{path::PathBuf, io::{Read, Write}};
|
||||
use std::{io::{Read, Write}, path::PathBuf, thread};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fs;
|
||||
use std::os::unix::net::{UnixListener, UnixStream};
|
||||
@@ -24,6 +24,9 @@ pub enum Action {
|
||||
GetStats,
|
||||
StartFanControl,
|
||||
StopFanControl,
|
||||
GetFanControl,
|
||||
SetFanCurve,
|
||||
Shutdown,
|
||||
}
|
||||
|
||||
impl Daemon {
|
||||
@@ -43,13 +46,13 @@ impl Daemon {
|
||||
Daemon { listener, gpu_controller: GpuController::new(PathBuf::from("/sys/class/drm/card0/device"))}
|
||||
}
|
||||
|
||||
pub fn listen(mut self) {
|
||||
pub fn listen(self) {
|
||||
for stream in self.listener.incoming() {
|
||||
match stream {
|
||||
Ok(stream) => {
|
||||
//let controller = self.gpu_controller.clone();
|
||||
//thread::spawn(move || Daemon::handle_connection(controller, stream));
|
||||
Daemon::handle_connection(&mut self.gpu_controller, stream);
|
||||
let mut controller = self.gpu_controller.clone();
|
||||
thread::spawn(move || Daemon::handle_connection(&mut controller, stream));
|
||||
//Daemon::handle_connection(&mut self.gpu_controller, stream);
|
||||
}
|
||||
Err(err) => {
|
||||
println!("Error: {}", err);
|
||||
@@ -62,16 +65,24 @@ impl Daemon {
|
||||
fn handle_connection(gpu_controller: &mut GpuController, mut stream: UnixStream) {
|
||||
let mut buffer: [u8; 4] = [0; 4];
|
||||
stream.read(&mut buffer).unwrap();
|
||||
println!("finished reading, buffer size {}", buffer.len());
|
||||
//println!("finished reading, buffer size {}", buffer.len());
|
||||
let action: Action = bincode::deserialize(&buffer).expect("Failed to deserialize buffer");
|
||||
println!("{:?}", action);
|
||||
//println!("{:?}", action);
|
||||
|
||||
let response: Option<Vec<u8>> = match action {
|
||||
Action::GetStats => Some(bincode::serialize(&gpu_controller.get_stats()).unwrap()),
|
||||
Action::GetInfo => Some(bincode::serialize(&gpu_controller.gpu_info).unwrap()),
|
||||
Action::StartFanControl => Some(bincode::serialize(&gpu_controller.start_fan_control()).unwrap()),
|
||||
Action::StopFanControl => Some(bincode::serialize(&gpu_controller.stop_fan_control()).unwrap()),
|
||||
Action::GetFanControl => Some(bincode::serialize(&gpu_controller.get_fan_control()).unwrap()),
|
||||
Action::SetFanCurve => {
|
||||
let mut buffer = Vec::new();
|
||||
stream.read_to_end(&mut buffer).unwrap();
|
||||
gpu_controller.set_fan_curve(bincode::deserialize(&buffer).expect("Failed to deserialize curve"));
|
||||
None
|
||||
},
|
||||
Action::CheckAlive => Some(vec![1]),
|
||||
Action::Shutdown => std::process::exit(0),
|
||||
};
|
||||
|
||||
if let Some(r) = &response {
|
||||
@@ -80,6 +91,7 @@ impl Daemon {
|
||||
.expect("Failed writing response");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -4,11 +4,11 @@ extern crate gtk;
|
||||
|
||||
use daemon::{Daemon, daemon_connection::DaemonConnection};
|
||||
use gio::prelude::*;
|
||||
use gtk::{ButtonsType, DialogFlags, Label, LevelBar, MessageType, prelude::*};
|
||||
use gtk::{Adjustment, Button, ButtonsType, DialogFlags, Frame, Label, LevelBar, MessageType, Switch, prelude::*};
|
||||
|
||||
use gtk::{Builder, MessageDialog, TextBuffer, Window};
|
||||
|
||||
use std::{env::args, thread, time::Duration};
|
||||
use std::{collections::BTreeMap, env::args, sync::{Arc, RwLock}, thread, time::Duration};
|
||||
|
||||
fn build_ui(application: >k::Application) {
|
||||
let glade_src = include_str!("main_window.glade");
|
||||
@@ -78,9 +78,23 @@ fn build_ui(application: >k::Application) {
|
||||
let fan_speed_text_buffer: TextBuffer = builder
|
||||
.get_object("fan_speed_text_buffer").unwrap();
|
||||
|
||||
let automatic_fan_control_switch: Switch = builder
|
||||
.get_object("automatic_fan_control_switch").unwrap();
|
||||
|
||||
let apply_button: Button = builder
|
||||
.get_object("apply_button").unwrap();
|
||||
|
||||
let fan_curve_frame: Frame = builder
|
||||
.get_object("fan_curve_frame").unwrap();
|
||||
|
||||
|
||||
let mut unpriviliged: bool = false;
|
||||
|
||||
let d = match DaemonConnection::new() {
|
||||
Ok(a) => a,
|
||||
Err(_) => {
|
||||
unpriviliged = true;
|
||||
|
||||
let daemon = Daemon::new();
|
||||
thread::spawn(move || {
|
||||
daemon.listen();
|
||||
@@ -91,7 +105,7 @@ fn build_ui(application: >k::Application) {
|
||||
DialogFlags::empty(),
|
||||
MessageType::Error,
|
||||
ButtonsType::Ok,
|
||||
"Running in unpriveleged mode",
|
||||
"Running in unpriviliged mode",
|
||||
);
|
||||
diag.run();
|
||||
diag.hide();
|
||||
@@ -117,6 +131,12 @@ fn build_ui(application: >k::Application) {
|
||||
vulkan_version_text_buffer.set_text(&gpu_info.vulkan_info.api_version);
|
||||
vulkan_features_text_buffer.set_text(&gpu_info.vulkan_info.features);
|
||||
|
||||
if unpriviliged {
|
||||
automatic_fan_control_switch.set_sensitive(false);
|
||||
fan_curve_frame.set_visible(false);
|
||||
automatic_fan_control_switch.set_tooltip_text(Some("Unavailable in unprivileged mode"));
|
||||
}
|
||||
|
||||
let (tx, rx) = glib::MainContext::channel(glib::PRIORITY_DEFAULT);
|
||||
|
||||
thread::spawn(move || {
|
||||
@@ -128,6 +148,7 @@ fn build_ui(application: >k::Application) {
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
rx.attach(None, move |gpu_stats| {
|
||||
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);
|
||||
@@ -146,18 +167,84 @@ fn build_ui(application: >k::Application) {
|
||||
|
||||
glib::Continue(true)
|
||||
});
|
||||
|
||||
|
||||
let fan_control = d.get_fan_control();
|
||||
|
||||
if fan_control.enabled {
|
||||
println!("Automatic fan control disabled!");
|
||||
automatic_fan_control_switch.set_active(false);
|
||||
fan_curve_frame.set_visible(true);
|
||||
}
|
||||
else {
|
||||
println!("Automatic fan control enabled");
|
||||
fan_curve_frame.set_visible(false);
|
||||
}
|
||||
|
||||
let b = apply_button.clone();
|
||||
|
||||
let switch = automatic_fan_control_switch.clone();
|
||||
automatic_fan_control_switch.connect_changed_active(move |_| {
|
||||
match switch.get_active() {
|
||||
true => {
|
||||
fan_curve_frame.set_visible(false);
|
||||
},
|
||||
false => {
|
||||
fan_curve_frame.set_visible(true);
|
||||
}
|
||||
}
|
||||
|
||||
b.set_sensitive(true);
|
||||
});
|
||||
|
||||
let curve: Arc<RwLock<BTreeMap<i32, f64>>> = Arc::new(RwLock::new(fan_control.curve));
|
||||
|
||||
for i in 1..6 {
|
||||
let curve_temperature_adjustment: Adjustment = builder
|
||||
.get_object(&format!("curve_temperature_adjustment_{}", i)).unwrap();
|
||||
|
||||
let value = *curve.read().unwrap().get(&(i * 20)).expect("Could not get by index");
|
||||
println!("Setting value {} on adjustment {}", value, i);
|
||||
curve_temperature_adjustment.set_value(value);
|
||||
|
||||
let c = curve.clone();
|
||||
let b = apply_button.clone();
|
||||
|
||||
curve_temperature_adjustment.connect_value_changed(move |adj | {
|
||||
c.write().unwrap().insert(20 * i, adj.get_value());
|
||||
b.set_sensitive(true);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
apply_button.connect_clicked(move |b| {
|
||||
let curve = curve.read().unwrap().clone();
|
||||
println!("setting curve to {:?}", curve);
|
||||
d.set_fan_curve(curve);
|
||||
b.set_sensitive(false);
|
||||
|
||||
match automatic_fan_control_switch.get_active() {
|
||||
true => {
|
||||
d.stop_fan_control().unwrap();
|
||||
},
|
||||
false => {
|
||||
d.start_fan_control().unwrap();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
main_window.set_application(Some(application));
|
||||
|
||||
main_window.show_all();
|
||||
main_window.show();
|
||||
}
|
||||
|
||||
|
||||
fn main() {
|
||||
println!("Initializing gtk");
|
||||
let application = gtk::Application::new(Some("com.ilyaz.yagc"), Default::default())
|
||||
.expect("failed to initialize");
|
||||
|
||||
application.connect_activate(|app| {
|
||||
println!("Activating");
|
||||
build_ui(app);
|
||||
});
|
||||
|
||||
|
||||
@@ -2,6 +2,31 @@
|
||||
<!-- Generated with glade 3.38.1 -->
|
||||
<interface>
|
||||
<requires lib="gtk+" version="3.24"/>
|
||||
<object class="GtkAdjustment" id="curve_temperature_adjustment_1">
|
||||
<property name="upper">100</property>
|
||||
<property name="step-increment">1</property>
|
||||
<property name="page-increment">10</property>
|
||||
</object>
|
||||
<object class="GtkAdjustment" id="curve_temperature_adjustment_2">
|
||||
<property name="upper">100</property>
|
||||
<property name="step-increment">1</property>
|
||||
<property name="page-increment">10</property>
|
||||
</object>
|
||||
<object class="GtkAdjustment" id="curve_temperature_adjustment_3">
|
||||
<property name="upper">100</property>
|
||||
<property name="step-increment">1</property>
|
||||
<property name="page-increment">10</property>
|
||||
</object>
|
||||
<object class="GtkAdjustment" id="curve_temperature_adjustment_4">
|
||||
<property name="upper">100</property>
|
||||
<property name="step-increment">1</property>
|
||||
<property name="page-increment">10</property>
|
||||
</object>
|
||||
<object class="GtkAdjustment" id="curve_temperature_adjustment_5">
|
||||
<property name="upper">100</property>
|
||||
<property name="step-increment">1</property>
|
||||
<property name="page-increment">10</property>
|
||||
</object>
|
||||
<object class="GtkTextBuffer" id="driver_text_buffer">
|
||||
<property name="text" translatable="yes">driver</property>
|
||||
</object>
|
||||
@@ -326,7 +351,7 @@
|
||||
<object class="GtkTextView">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="margin-top">6</property>
|
||||
<property name="margin-top">7</property>
|
||||
<property name="margin-bottom">5</property>
|
||||
<property name="buffer">vulkan_device_name_text_buffer</property>
|
||||
<property name="monospace">True</property>
|
||||
@@ -340,7 +365,7 @@
|
||||
<object class="GtkTextView">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="margin-top">6</property>
|
||||
<property name="margin-top">7</property>
|
||||
<property name="margin-bottom">5</property>
|
||||
<property name="buffer">vulkan_version_text_buffer</property>
|
||||
<property name="monospace">True</property>
|
||||
@@ -751,7 +776,341 @@
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
<object class="GtkBox">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="halign">center</property>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="margin-start">5</property>
|
||||
<property name="margin-end">5</property>
|
||||
<property name="margin-top">5</property>
|
||||
<property name="margin-bottom">5</property>
|
||||
<property name="label" translatable="yes">Automatic fan control</property>
|
||||
<attributes>
|
||||
<attribute name="weight" value="bold"/>
|
||||
</attributes>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSwitch" id="automatic_fan_control_switch">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="active">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkFrame" id="fan_curve_frame">
|
||||
<property name="can-focus">False</property>
|
||||
<property name="margin-start">10</property>
|
||||
<property name="margin-end">10</property>
|
||||
<property name="margin-top">10</property>
|
||||
<property name="margin-bottom">10</property>
|
||||
<property name="vexpand">True</property>
|
||||
<property name="label-xalign">0.15000000596046448</property>
|
||||
<property name="shadow-type">out</property>
|
||||
<child>
|
||||
<!-- n-columns=7 n-rows=3 -->
|
||||
<object class="GtkGrid">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="label" translatable="yes">PWM %</property>
|
||||
<property name="angle">90</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left-attach">0</property>
|
||||
<property name="top-attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="label" translatable="yes">Temperature °C</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left-attach">2</property>
|
||||
<property name="top-attach">2</property>
|
||||
<property name="width">5</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="vexpand">True</property>
|
||||
<property name="label" translatable="yes">100</property>
|
||||
<property name="angle">90</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="vexpand">True</property>
|
||||
<property name="label" translatable="yes">75</property>
|
||||
<property name="angle">90</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="vexpand">True</property>
|
||||
<property name="label" translatable="yes">50</property>
|
||||
<property name="angle">90</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="vexpand">True</property>
|
||||
<property name="label" translatable="yes">25</property>
|
||||
<property name="angle">90</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="vexpand">True</property>
|
||||
<property name="label" translatable="yes">0</property>
|
||||
<property name="angle">90</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left-attach">1</property>
|
||||
<property name="top-attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkScale">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="hexpand">True</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="adjustment">curve_temperature_adjustment_1</property>
|
||||
<property name="inverted">True</property>
|
||||
<property name="restrict-to-fill-level">False</property>
|
||||
<property name="fill-level">0</property>
|
||||
<property name="round-digits">1</property>
|
||||
<property name="digits">0</property>
|
||||
<property name="draw-value">False</property>
|
||||
<property name="value-pos">left</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left-attach">2</property>
|
||||
<property name="top-attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="label" translatable="yes">20</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left-attach">2</property>
|
||||
<property name="top-attach">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkScale">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="hexpand">True</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="adjustment">curve_temperature_adjustment_2</property>
|
||||
<property name="inverted">True</property>
|
||||
<property name="restrict-to-fill-level">False</property>
|
||||
<property name="fill-level">0</property>
|
||||
<property name="round-digits">1</property>
|
||||
<property name="digits">0</property>
|
||||
<property name="draw-value">False</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left-attach">3</property>
|
||||
<property name="top-attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="label" translatable="yes">40</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left-attach">3</property>
|
||||
<property name="top-attach">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkScale">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="hexpand">True</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="adjustment">curve_temperature_adjustment_3</property>
|
||||
<property name="inverted">True</property>
|
||||
<property name="round-digits">1</property>
|
||||
<property name="digits">0</property>
|
||||
<property name="draw-value">False</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left-attach">4</property>
|
||||
<property name="top-attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="label" translatable="yes">60</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left-attach">4</property>
|
||||
<property name="top-attach">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkScale">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="hexpand">True</property>
|
||||
<property name="vexpand">True</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="adjustment">curve_temperature_adjustment_4</property>
|
||||
<property name="inverted">True</property>
|
||||
<property name="round-digits">1</property>
|
||||
<property name="digits">0</property>
|
||||
<property name="draw-value">False</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left-attach">5</property>
|
||||
<property name="top-attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="label" translatable="yes">80</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left-attach">5</property>
|
||||
<property name="top-attach">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkScale">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="hexpand">True</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="adjustment">curve_temperature_adjustment_5</property>
|
||||
<property name="inverted">True</property>
|
||||
<property name="round-digits">1</property>
|
||||
<property name="digits">0</property>
|
||||
<property name="draw-value">False</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left-attach">6</property>
|
||||
<property name="top-attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="label" translatable="yes">100</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left-attach">6</property>
|
||||
<property name="top-attach">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child type="label">
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="label" translatable="yes">Fan Curve</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
@@ -769,6 +1128,19 @@
|
||||
<property name="tab-fill">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child type="action-end">
|
||||
<object class="GtkButton" id="apply_button">
|
||||
<property name="label">gtk-apply</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="sensitive">False</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="receives-default">True</property>
|
||||
<property name="use-stock">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="tab-fill">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
|
||||
Reference in New Issue
Block a user