New UI base

This commit is contained in:
Ilya Zlobintsev
2021-01-30 08:35:01 +02:00
parent 573d287d09
commit 43641811b3
7 changed files with 166 additions and 2244 deletions

View File

@@ -7,9 +7,5 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
daemon = { path = "../daemon" }
gtk = "0.9"
gio = "0.9"
glib = "0.10"
gdk = "0.13"
pango = "0.9"
gtk = { version = "0.9", features = ["v3_22"] }
daemon = { path = "../daemon" }

54
gui/src/app.rs Normal file
View File

@@ -0,0 +1,54 @@
mod header;
mod root_stack;
extern crate gtk;
use daemon::daemon_connection::DaemonConnection;
use gtk::*;
use header::Header;
use root_stack::RootStack;
pub struct App {
pub window: Window,
pub header: Header,
root_stack: RootStack,
}
impl App {
pub fn new() -> Self {
let window = Window::new(WindowType::Toplevel);
let header = Header::new();
window.set_titlebar(Some(&header.container));
window.set_title("LACT");
// window.set_wmclass("lact", "LACT");
window.set_size_request(500, 600);
window.connect_delete_event(move |_, _| {
main_quit();
Inhibit(false)
});
let root_stack = RootStack::new();
header.set_switcher_stack(&root_stack.container);
window.add(&root_stack.container);
App { window, header, root_stack }
}
pub fn run(&self, daemon_connection: DaemonConnection) {
self.window.show_all();
let gpus = daemon_connection.get_gpus().unwrap();
let gpu_info = daemon_connection.get_gpu_info(*gpus.iter().next().unwrap().0).unwrap();
self.root_stack.info_page.set_info(gpu_info);
gtk::main();
}
}

30
gui/src/app/header.rs Normal file
View File

@@ -0,0 +1,30 @@
use std::env;
use gtk::*;
pub struct Header {
pub container: HeaderBar,
switcher: StackSwitcher,
}
impl Header {
pub fn new() -> Self {
let container = HeaderBar::new();
// container.set_title(Some("LACT"));
if env::var("XDG_CURRENT_DESKTOP") == Ok("GNOME".to_string()) {
container.set_show_close_button(true);
}
let switcher = StackSwitcher::new();
container.pack_start(&switcher);
Header { container, switcher }
}
pub fn set_switcher_stack(&self, stack: &Stack) {
self.switcher.set_stack(Some(stack));
}
}

22
gui/src/app/root_stack.rs Normal file
View File

@@ -0,0 +1,22 @@
mod info_page;
use gtk::*;
use info_page::InformationPage;
pub struct RootStack {
pub container: Stack,
pub info_page: InformationPage,
}
impl RootStack {
pub fn new() -> Self {
let container = Stack::new();
let info_page = InformationPage::new();
container.add_titled(&info_page.container, "info_page", "Information");
Self { container, info_page }
}
}

View File

@@ -0,0 +1,26 @@
use daemon::gpu_controller::GpuInfo;
use gtk::*;
pub struct InformationPage {
pub container: Box,
gpu_name_label: Label,
gpu_manufacturer_label: Label,
}
impl InformationPage {
pub fn new() -> Self {
let container = Box::new(Orientation::Vertical, 5);
let gpu_name_label = Label::new(None);
let gpu_manufacturer_label = Label::new(None);
container.add(&gpu_name_label);
container.add(&gpu_manufacturer_label);
Self { container, gpu_name_label, gpu_manufacturer_label }
}
pub fn set_info(&self, gpu_info: GpuInfo) {
self.gpu_name_label.set_text(&gpu_info.vendor_data.card_model.unwrap_or_default());
self.gpu_manufacturer_label.set_text(&gpu_info.vendor_data.card_vendor.unwrap_or_default());
}
}

View File

@@ -1,576 +1,48 @@
extern crate gdk;
extern crate gio;
extern crate gtk;
use std::thread;
use daemon::{daemon_connection::DaemonConnection, gpu_controller::PowerProfile, Daemon};
use gio::prelude::*;
use gtk::{Adjustment, Button, ButtonsType, ComboBoxText, DialogFlags, Frame, Label, LevelBar, MessageType, Notebook, Scale, Switch, prelude::*};
use app::App;
use daemon::{Daemon, daemon_connection::DaemonConnection};
use gtk::*;
use gtk::{Builder, MessageDialog, TextBuffer, Window};
use pango::EllipsizeMode;
mod app;
use std::{collections::BTreeMap, env::args, fs, sync::{Arc, Mutex, RwLock}, thread, time::Duration};
fn main() {
if gtk::init().is_err() {
panic!("Cannot initialize GTK");
}
let app = App::new();
app.run(connect_daemon());
}
fn build_ui(application: &gtk::Application) {
let glade_src = include_str!("main_window.glade");
let builder = Builder::from_string(glade_src);
println!("Getting elements");
fn connect_daemon() -> DaemonConnection {
match DaemonConnection::new() {
Ok(connection) => {
println!("Connection to daemon established");
connection
},
Err(e) => {
println!("Error {:?} connecting to daemon", e);
println!("Starting unprivileged daemon instance");
let main_window: Window = builder
.get_object("main_window")
.expect("Couldn't get main_window");
let vram_usage_level_bar: LevelBar = builder
.get_object("vram_usage_level_bar")
.expect("Couldnt get levelbar");
let vram_usage_label: Label = builder
.get_object("vram_usage_label")
.expect("Couldn't get label");
let gpu_select_comboboxtext: ComboBoxText =
builder.get_object("gpu_select_comboboxtext").unwrap();
let gpu_clock_text_buffer: TextBuffer = builder.get_object("gpu_clock_text_buffer").unwrap();
let vram_clock_text_buffer: TextBuffer = builder.get_object("vram_clock_text_buffer").unwrap();
let gpu_temp_text_buffer: TextBuffer = builder.get_object("gpu_temp_text_buffer").unwrap();
let gpu_power_text_buffer: TextBuffer = builder.get_object("gpu_power_text_buffer").unwrap();
let fan_speed_text_buffer: TextBuffer = builder.get_object("fan_speed_text_buffer").unwrap();
let power_cap_label: Label = builder.get_object("power_cap_label").unwrap();
let apply_button: Button = builder.get_object("apply_button").unwrap();
let automatic_fan_control_switch: Switch =
builder.get_object("automatic_fan_control_switch").unwrap();
let fan_curve_frame: Frame = builder.get_object("fan_curve_frame").unwrap();
let gpu_power_adjustment: Adjustment = builder.get_object("gpu_power_adjustment").unwrap();
let gpu_voltage_text_buffer: TextBuffer = builder.get_object("gpu_voltage_text_buffer").unwrap();
let power_profile_select_comboboxtext: ComboBoxText = builder
.get_object("power_profile_select_comboboxtext")
.unwrap();
let power_profile_description_label: Label = builder.get_object("power_profile_description_label").unwrap();
let gpu_clockspeed_adjustment: Adjustment = builder.get_object("gpu_clockspeed_adjustment").unwrap();
let vram_clockspeed_adjustment: Adjustment = builder.get_object("vram_clockspeed_adjustment").unwrap();
let gpu_voltage_adjustment: Adjustment = builder.get_object("gpu_voltage_adjustment").unwrap();
let vram_voltage_adjustment: Adjustment = builder.get_object("vram_voltage_adjustment").unwrap();
let reset_clocks_button: Button = builder.get_object("reset_clocks_button").unwrap();
let power_cap_scale: Scale = builder.get_object("power_cap_scale").unwrap();
let clocks_notebook: Notebook = builder.get_object("clocks_notebook").unwrap();
let clocks_unsupported_label: Label = builder.get_object("clocks_unsupported_label").unwrap();
let mut unpriviliged: bool = false;
let d = match DaemonConnection::new() {
Ok(a) => a,
Err(_) => {
unpriviliged = true;
let daemon = Daemon::new(unpriviliged);
thread::spawn(move || {
let daemon = Daemon::new(true);
daemon.listen();
});
let diag = MessageDialog::new(
None::<&Window>,
let dialog = MessageDialog::new(
None::<&gtk::Window>,
DialogFlags::empty(),
MessageType::Error,
ButtonsType::Ok,
"Running in unpriviliged mode",
);
diag.run();
diag.hide();
gtk::MessageType::Warning,
gtk::ButtonsType::Ok,
"Unable to connect to daemon. Running in unprivileged mode.");
dialog.run();
dialog.hide();
DaemonConnection::new().unwrap()
}
};
println!("Connected");
let gpus = d.get_gpus().unwrap();
for (id, gpu) in &gpus {
gpu_select_comboboxtext.append(Some(&id.to_string()), &gpu.clone().unwrap_or_default());
}
//limits the length of gpu names in combobox
for cell in gpu_select_comboboxtext.get_cells() {
cell.set_property("width-chars", &10).unwrap();
cell.set_property("ellipsize", &EllipsizeMode::End).unwrap();
}
let current_gpu_id = Arc::new(RwLock::new(0u32));
{ //Handle power limit adjustment change
let apply_button = apply_button.clone();
gpu_power_adjustment.connect_value_changed(move |adjustment| {
println!(
"changed adjustment value to {}/{}",
adjustment.get_value(),
adjustment.get_upper()
);
apply_button.set_sensitive(true);
power_cap_label.set_text(&format!(
"{}/{}",
adjustment.get_value().floor(),
adjustment.get_upper()
));
});
}
let adjs = [gpu_clockspeed_adjustment.clone(), vram_clockspeed_adjustment.clone(), gpu_voltage_adjustment.clone(), vram_voltage_adjustment.clone()];
for adjustment in adjs.iter() {
let b = apply_button.clone();
adjustment.connect_value_changed(move |_| {
b.set_sensitive(true);
});
}
{ //Handle changing the GPU power profile
let b = apply_button.clone();
let description_label = power_profile_description_label.clone();
power_profile_select_comboboxtext.connect_changed(move |combobox| {
println!("power profile selection changed");
b.set_sensitive(true);
match combobox.get_active().unwrap() {
0 => description_label.set_text("Automatically adjust core and VRAM clocks. (Default)"),
1 => description_label.set_text("Always run the on the highest clocks."),
2 => description_label.set_text("Always run the on the lowest clocks."),
_ => unreachable!(),
}
});
}
let gpu_power_level: Arc<Mutex<Option<u32>>> = Arc::new(Mutex::new(None));
let vram_power_level: Arc<Mutex<Option<u32>>> = Arc::new(Mutex::new(None));
{ // Handle when the GPU is chosen from the dropdown box (also triggered on initializtion)
let (gpu_power_level, vram_power_level) = (gpu_power_level.clone(), vram_power_level.clone());
let builder = builder.clone();
let current_gpu_id = current_gpu_id.clone();
gpu_select_comboboxtext.connect_changed(move |combobox| {
let mut current_gpu_id = current_gpu_id.write().unwrap();
*current_gpu_id = combobox
.get_active_id()
.unwrap()
.parse::<u32>()
.expect("invalid id");
println!("Set current gpu id to {}", current_gpu_id);
set_info(&builder, d, current_gpu_id.clone(), &gpu_power_level, &vram_power_level);
});
}
{ //Handle reset clocks button
let current_gpu_id = current_gpu_id.clone();
let (gpu_power_level, vram_power_level) = (gpu_power_level.clone(), vram_power_level.clone());
let builder = builder.clone();
let apply_button = apply_button.clone();
reset_clocks_button.connect_clicked(move |_| {
let current_gpu_id = *current_gpu_id.read().unwrap();
d.reset_gpu_power_states(current_gpu_id).unwrap();
set_info(&builder, d, current_gpu_id, &gpu_power_level, &vram_power_level);
apply_button.set_sensitive(true);
});
}
{ //Apply button click
let current_gpu_id = current_gpu_id.clone();
let auto_fan_control_switch = automatic_fan_control_switch.clone();
let power_profile_select_comboboxtext = power_profile_select_comboboxtext.clone();
let (gpu_power_level, vram_power_level) = (gpu_power_level.clone(), vram_power_level.clone());
let builder = builder.clone();
apply_button.connect_clicked(move |_| {
let gpu_id = *current_gpu_id.read().unwrap();
let mut curve: BTreeMap<i64, f64> = BTreeMap::new();
for i in 1..6 {
let curve_temperature_adjustment: Adjustment = builder
.get_object(&format!("curve_temperature_adjustment_{}", i))
.unwrap();
curve.insert(20 * i, curve_temperature_adjustment.get_value());
}
println!("setting curve to {:?}", curve);
d.set_fan_curve(gpu_id, curve).unwrap();
match auto_fan_control_switch.get_active() {
true => {
d.stop_fan_control(gpu_id).unwrap();
}
false => {
d.start_fan_control(gpu_id).unwrap();
}
}
let power_cap = gpu_power_adjustment.get_value().floor() as i64;
d.set_power_cap(gpu_id, power_cap).unwrap();
d.set_power_profile(gpu_id, PowerProfile::from_str(&power_profile_select_comboboxtext.get_active_text().unwrap()).unwrap()).unwrap();
if let Some(gpu_power_level) = *gpu_power_level.lock().unwrap() {
d.set_gpu_power_state(gpu_id, gpu_power_level, gpu_clockspeed_adjustment.get_value() as i64, Some((gpu_voltage_adjustment.get_value() * 1000.0) as i64)).unwrap();
if let Some(vram_power_level) = *vram_power_level.lock().unwrap() {
d.set_vram_power_state(gpu_id, vram_power_level, vram_clockspeed_adjustment.get_value() as i64, Some((vram_voltage_adjustment.get_value() * 1000.0) as i64)).unwrap();
}
d.commit_gpu_power_states(gpu_id).unwrap();
}
set_info(&builder, d, gpu_id, &gpu_power_level, &vram_power_level);
});
}
//gpu_select_comboboxtext.set_active_id(Some(&current_gpu_id.to_string()));
gpu_select_comboboxtext.set_active(Some(0));
if unpriviliged {
automatic_fan_control_switch.set_sensitive(false);
fan_curve_frame.set_visible(false);
power_profile_select_comboboxtext.set_sensitive(false);
power_cap_scale.set_sensitive(false);
clocks_notebook.set_visible(false);
clocks_unsupported_label.set_visible(true);
automatic_fan_control_switch.set_tooltip_text(Some("Unavailable in unprivileged mode"));
}
let (tx, rx) = glib::MainContext::channel(glib::PRIORITY_DEFAULT);
let cur_gpu_id = current_gpu_id.clone();
thread::spawn(move || loop {
let current_gpu_id = *cur_gpu_id.clone().read().unwrap();
println!("Getting stats for {}", current_gpu_id);
let gpu_stats = d.get_gpu_stats(current_gpu_id).unwrap();
tx.send(gpu_stats).expect("Couldn't send text");
thread::sleep(Duration::from_millis(500));
});
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);
let text = format!("{}/{} MiB", gpu_stats.mem_used, gpu_stats.mem_total);
vram_usage_label.set_text(&text);
gpu_clock_text_buffer.set_text(&format!("{}MHz", gpu_stats.gpu_freq));
vram_clock_text_buffer.set_text(&format!("{}MHz", gpu_stats.mem_freq));
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_cap));
fan_speed_text_buffer.set_text(&format!(
"{}RPM({}%)",
gpu_stats.fan_speed,
(gpu_stats.fan_speed as f64 / gpu_stats.max_fan_speed as f64 * 100 as f64) as i64
));
gpu_voltage_text_buffer.set_text(&format!("{}V", gpu_stats.voltage as f64 / 1000.0));
glib::Continue(true)
});
let b = apply_button.clone();
{
let automatic_fan_control_switch = automatic_fan_control_switch.clone();
automatic_fan_control_switch.connect_changed_active(move |switch| {
b.set_sensitive(true);
match switch.get_active() {
true => {
fan_curve_frame.set_visible(false);
let diag = MessageDialog::new(
None::<&Window>,
DialogFlags::empty(),
MessageType::Error,
ButtonsType::Ok,
"WARNING: Due to a driver bug, the GPU fan may misbehave after switching to automatic control. You may need to reboot your system to avoid issues.",
);
diag.connect_response(|diag, _| {
diag.hide();
});
diag.show_all();
}
false => {
fan_curve_frame.set_visible(true);
}
}
});
}
main_window.set_application(Some(application));
main_window.show();
}
fn set_info(builder: &Builder, d: DaemonConnection, gpu_id: u32, gpu_power_level: &Arc<Mutex<Option<u32>>>, vram_power_level: &Arc<Mutex<Option<u32>>>) {
let gpu_model_text_buffer: TextBuffer = builder
.get_object("gpu_model_text_buffer")
.expect("Couldn't get textbuffer");
let vbios_version_text_buffer: TextBuffer = builder
.get_object("vbios_version_text_buffer")
.expect("Couldn't get textbuffer");
let driver_text_buffer: TextBuffer = builder
.get_object("driver_text_buffer")
.expect("Couldn't get textbuffer");
let manufacturer_text_buffer: TextBuffer = builder
.get_object("manufacturer_text_buffer")
.expect("Couldn't get textbuffer");
let vram_size_text_buffer: TextBuffer = builder
.get_object("vram_size_text_buffer")
.expect("Couldn't get textbuffer");
let link_speed_text_buffer: TextBuffer = builder
.get_object("link_speed_text_buffer")
.expect("Couldn't get textbuffer");
let vulkan_device_name_text_buffer: TextBuffer = builder
.get_object("vulkan_device_name_text_buffer")
.expect("Couldn't get textbuffer");
let vulkan_version_text_buffer: TextBuffer = builder
.get_object("vulkan_version_text_buffer")
.expect("Couldn't get textbuffer");
let vulkan_features_text_buffer: TextBuffer = builder
.get_object("vulkan_features_text_buffer")
.expect("Couldn't get textbuffer");
let automatic_fan_control_switch: Switch =
builder.get_object("automatic_fan_control_switch").unwrap();
let fan_curve_frame: Frame = builder.get_object("fan_curve_frame").unwrap();
let gpu_power_adjustment: Adjustment = builder.get_object("gpu_power_adjustment").unwrap();
let apply_button: Button = builder.get_object("apply_button").unwrap();
let overclocking_info_frame: Frame = builder.get_object("overclocking_info_frame").unwrap();
let gpu_clockspeed_adjustment: Adjustment = builder.get_object("gpu_clockspeed_adjustment").unwrap();
let vram_clockspeed_adjustment: Adjustment = builder.get_object("vram_clockspeed_adjustment").unwrap();
let gpu_voltage_adjustment: Adjustment = builder.get_object("gpu_voltage_adjustment").unwrap();
let vram_voltage_adjustment: Adjustment = builder.get_object("vram_voltage_adjustment").unwrap();
let clocks_notebook: Notebook = builder.get_object("clocks_notebook").unwrap();
let clocks_unsupported_label: Label = builder.get_object("clocks_unsupported_label").unwrap();
let power_cap_scale: Scale = builder.get_object("power_cap_scale").unwrap();
//let power_levels_box: gtk::Box = builder.get_object("power_levels_box").unwrap();
let power_profile_select_comboboxtext: ComboBoxText = builder
.get_object("power_profile_select_comboboxtext")
.unwrap();
match fs::read_to_string("/proc/cmdline") {
Ok(cmdline) => {
if cmdline.contains("amdgpu.ppfeaturemask=") {
overclocking_info_frame.set_visible(false);
}
}
Err(_) => (),
}
let gpu_info = d.get_gpu_info(gpu_id).unwrap();
if let Some(card_model) = gpu_info.vendor_data.card_model {
gpu_model_text_buffer.set_text(&card_model);
}
else {
gpu_model_text_buffer.set_text(&gpu_info.vendor_data.gpu_model.unwrap_or_default());
}
manufacturer_text_buffer.set_text(&gpu_info.vendor_data.card_vendor.unwrap_or_default());
vbios_version_text_buffer.set_text(&gpu_info.vbios_version);
driver_text_buffer.set_text(&gpu_info.driver);
vram_size_text_buffer.set_text(&format!("{} MiB", &gpu_info.vram_size));
link_speed_text_buffer.set_text(&format!(
"{} x{}",
&gpu_info.link_speed, &gpu_info.link_width
));
let vulkan_features = gpu_info
.vulkan_info
.features
.replace(',', "\n")
.replace("Features", "")
.replace("{", "")
.replace("}", "")
.replace(" ", "")
.replace(":", ": ");
vulkan_device_name_text_buffer.set_text(&gpu_info.vulkan_info.device_name);
vulkan_version_text_buffer.set_text(&gpu_info.vulkan_info.api_version);
vulkan_features_text_buffer.set_text(&vulkan_features);
if let Ok((power_cap, power_cap_max)) = d.get_power_cap(gpu_id) {
gpu_power_adjustment.set_upper(power_cap_max as f64);
gpu_power_adjustment.set_value(power_cap as f64);
}
else {
power_cap_scale.set_sensitive(false);
}
match &gpu_info.power_profile {
Some(power_profile) => {
power_profile_select_comboboxtext.set_sensitive(true);
power_profile_select_comboboxtext.set_active(match power_profile {
PowerProfile::Auto => Some(0),
PowerProfile::High => Some(1),
PowerProfile::Low => Some(2),
});
},
None => {
power_profile_select_comboboxtext.set_sensitive(false);
}
}
let fan_control = d.get_fan_control(gpu_id);
match fan_control {
Ok(ref 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");
automatic_fan_control_switch.set_active(true);
fan_curve_frame.set_visible(false);
}
}
Err(_) => {
automatic_fan_control_switch.set_sensitive(false);
automatic_fan_control_switch.set_tooltip_text(Some("Unavailable"));
fan_curve_frame.set_visible(false);
}
}
match fan_control {
Ok(fan_control) => {
//let curve: Arc<RwLock<BTreeMap<i64, 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 = *fan_control.curve
.get(&(i * 20))
.expect("Could not get by index");
println!("Setting value {} on adjustment {}", value, i);
curve_temperature_adjustment.set_value(value);
let b = apply_button.clone();
curve_temperature_adjustment.connect_value_changed(move |_| {
b.set_sensitive(true);
});
}
}
Err(_) => (),
}
/*match gpu_info.clocks_table {
Some(clocks_table) => {
for (id, (clockspeed, voltage)) in clocks_table.gpu_power_levels {
let adjustment = Adjustment::new(clockspeed as f64,
clocks_table.gpu_clocks_range.0 as f64,
clocks_table.gpu_clocks_range.1 as f64,
1f64, 1f64, 1f64);
let scale = Scale::new(Orientation::Vertical, Some(&adjustment));
power_levels_box.pack_end(&scale, true, true, 5);
}
power_levels_box.show_all();
},
None => (),
}*/
match gpu_info.clocks_table {
Some(clocks_table) => {
gpu_clockspeed_adjustment.set_lower(clocks_table.gpu_clocks_range.0 as f64);
gpu_clockspeed_adjustment.set_upper(clocks_table.gpu_clocks_range.1 as f64);
vram_clockspeed_adjustment.set_lower(clocks_table.mem_clocks_range.0 as f64);
vram_clockspeed_adjustment.set_upper(clocks_table.mem_clocks_range.1 as f64);
gpu_voltage_adjustment.set_lower(clocks_table.voltage_range.0 as f64 / 1000.0);
gpu_voltage_adjustment.set_upper(clocks_table.voltage_range.1 as f64 / 1000.0);
let (gpu_power_level_id, (gpu_clockspeed, gpu_voltage)) = clocks_table.gpu_power_levels.iter().next_back().unwrap();
let (vram_power_level_id, (vram_clockspeed, vram_voltage)) = clocks_table.mem_power_levels.iter().next_back().unwrap();
gpu_clockspeed_adjustment.set_value(*gpu_clockspeed as f64);
vram_clockspeed_adjustment.set_value(*vram_clockspeed as f64);
gpu_voltage_adjustment.set_value(*gpu_voltage as f64 / 1000.0);
vram_voltage_adjustment.set_upper(*vram_voltage as f64 / 1000.0);
vram_voltage_adjustment.set_value(*vram_voltage as f64 / 1000.0);
gpu_power_level.lock().unwrap().replace(*gpu_power_level_id);
vram_power_level.lock().unwrap().replace(*vram_power_level_id);
clocks_notebook.set_visible(true);
clocks_unsupported_label.set_visible(false);
},
None => {
clocks_notebook.set_visible(false);
clocks_unsupported_label.set_visible(true);
},
}
apply_button.set_sensitive(false);
}
fn main() {
println!("Initializing gtk");
let application = gtk::Application::new(Some("com.ilyaz.lact"), Default::default())
.expect("failed to initialize");
application.connect_activate(|app| {
println!("Activating");
build_ui(app);
});
application.run(&args().collect::<Vec<_>>());
}

File diff suppressed because it is too large Load Diff