use crate::{KernelBranch, RunningKernelInfo}; use adw::prelude::*; use adw::ExpanderRow; use async_channel::Receiver; use duct::cmd; use glib::property::PropertyGet; use glib::*; use gtk::prelude::*; use gtk::*; use homedir::get_my_home; use std::cell::RefCell; use std::fs; use std::path::Path; use std::process::{Command, Stdio}; use std::rc::Rc; use version_compare::Version; use Vec; pub fn content( content_stack: >k::Stack, selected_kernel_branch: &Rc>, ) -> gtk::Box { let running_kernel_info = get_running_kernel_info(); let (get_kernel_branches_sender, get_kernel_branches_receiver) = async_channel::unbounded(); let get_kernel_branches_sender = get_kernel_branches_sender.clone(); std::thread::spawn(move || { get_kernel_branches_sender .send_blocking(get_kernel_branches()) .expect("channel needs to be open."); }); let content_box = gtk::Box::builder() .hexpand(true) .vexpand(true) .orientation(Orientation::Vertical) .build(); let tux_icon = gtk::Image::builder() .pixel_size(128) .halign(Align::Center) .hexpand(true) .margin_start(10) .margin_end(10) .margin_bottom(20) .margin_top(20) .build(); tux_icon.set_icon_name(Some("tux-symbolic")); tux_icon.add_css_class("symbolic-accent-bg"); let kernel_badge_box = gtk::Box::builder() .hexpand(true) .vexpand(true) .orientation(Orientation::Vertical) .build(); let kernel_branch_expander_row = adw::ExpanderRow::builder() .subtitle("Kernel Branch") .build(); kernel_branch_expander_row.add_row(&kernel_branch_expandable( &kernel_branch_expander_row, selected_kernel_branch, get_kernel_branches_receiver.clone(), )); let kernel_branch_expander_row_boxedlist = gtk::ListBox::builder() .selection_mode(SelectionMode::None) .hexpand(true) .vexpand(true) .halign(Align::Center) .margin_start(10) .margin_end(10) .margin_bottom(20) .margin_top(20) .build(); let button_box = gtk::Box::builder() .orientation(Orientation::Horizontal) .margin_start(10) .margin_end(10) .margin_bottom(20) .margin_top(20) .hexpand(true) .halign(Align::Center) .build(); let browse_kernels_button = gtk::Button::builder() .icon_name("web") .halign(Align::Start) .margin_start(10) .margin_end(10) .height_request(50) .width_request(50) .tooltip_text("Browse Kernel for select branch") .hexpand(true) .build(); browse_kernels_button.add_css_class("circular"); browse_kernels_button.connect_clicked(clone!(@weak content_stack => move |_| { content_stack.set_visible_child_name("kernel_pkg_page") })); let config_kernel_button = gtk::Button::builder() .icon_name("settings") .halign(Align::End) .margin_start(10) .margin_end(10) .height_request(50) .width_request(50) .tooltip_text("Configure Sched_EXT settings") .hexpand(true) .build(); config_kernel_button.add_css_class("circular"); config_kernel_button.connect_clicked(clone!(@weak content_stack => move |_| { content_stack.set_visible_child_name("sched_ext_page") })); button_box.append(&browse_kernels_button); button_box.append(&config_kernel_button); kernel_branch_expander_row_boxedlist.add_css_class("boxed-list"); kernel_branch_expander_row_boxedlist.append(&kernel_branch_expander_row); create_kernel_badges(&kernel_badge_box, &running_kernel_info); content_box.append(&kernel_badge_box); content_box.append(&tux_icon); content_box.append(&kernel_branch_expander_row_boxedlist); content_box.append(&button_box); let get_kernel_branches_loop_context = MainContext::default(); // The main loop executes the asynchronous block get_kernel_branches_loop_context.spawn_local( clone!(@strong selected_kernel_branch => async move { while let Ok(data) = get_kernel_branches_receiver.recv().await { let selected_kernel_branch_clone0 = selected_kernel_branch.clone(); let selected_kernel_branch_clone1 = selected_kernel_branch.clone(); match get_my_home() .unwrap() .unwrap() .join(".config/fedora-kernel-manager/branch") .exists() { true => { let narrow_branch: Vec = data .clone() .into_iter() .filter(|a| { a.name == fs::read_to_string( get_my_home() .unwrap() .unwrap() .join(".config/fedora-kernel-manager/branch"), ) .unwrap() }) .collect(); *selected_kernel_branch_clone0.borrow_mut() = narrow_branch.get(0).unwrap().clone() } _ => { let normal_branch = data.clone().get(0).unwrap().clone(); *selected_kernel_branch_clone0.borrow_mut() = normal_branch } }; } }), ); content_box } fn kernel_branch_expandable( expander_row: &adw::ExpanderRow, selected_kernel_branch: &Rc>, get_kernel_branches_receiver: Receiver>, ) -> gtk::ListBox { let searchbar = gtk::SearchEntry::builder().search_delay(500).build(); searchbar.add_css_class("round-border-only-top"); let boxedlist = gtk::ListBox::builder() .selection_mode(SelectionMode::None) .build(); boxedlist.append(&searchbar); let branch_container = gtk::ListBox::builder() .selection_mode(SelectionMode::None) .build(); branch_container.add_css_class("boxed-list"); let null_checkbutton = gtk::CheckButton::builder() .label("No branch selected") .build(); let get_kernel_branches_loop_context = MainContext::default(); // The main loop executes the asynchronous block get_kernel_branches_loop_context.spawn_local(clone!(@weak expander_row, @weak branch_container, @strong selected_kernel_branch => async move { while let Ok(data) = get_kernel_branches_receiver.recv().await { for branch in data { let branch_clone0 = branch.clone(); let branch_clone1 = branch.clone(); let branch_checkbutton = gtk::CheckButton::builder() .valign(Align::Center) .can_focus(false) .build(); let branch_row = adw::ActionRow::builder() .activatable_widget(&branch_checkbutton) .title(branch.name) .build(); branch_row.add_prefix(&branch_checkbutton); branch_checkbutton.set_group(Some(&null_checkbutton)); branch_container.append(&branch_row); let selected_kernel_branch_clone0 = selected_kernel_branch.clone(); branch_checkbutton.connect_toggled( clone!(@weak branch_checkbutton, @weak expander_row => move |_| { if branch_checkbutton.is_active() == true { expander_row.set_title(&branch_row.title()); save_branch_config(&branch_row.title().to_string()); *selected_kernel_branch_clone0.borrow_mut()=branch_clone0.clone() } }), ); match get_my_home() .unwrap() .unwrap() .join(".config/fedora-kernel-manager/branch") .exists() { true if fs::read_to_string( get_my_home() .unwrap() .unwrap() .join(".config/fedora-kernel-manager/branch"), ) .unwrap() == branch_clone1.name => { branch_checkbutton.set_active(true) } _ => {} } } } })); let branch_container_viewport = gtk::ScrolledWindow::builder() .child(&branch_container) .hscrollbar_policy(PolicyType::Never) .build(); branch_container.add_css_class("round-border-only-bottom"); boxedlist.append(&branch_container_viewport); searchbar.connect_search_changed(clone!(@weak searchbar, @weak branch_container => move |_| { let mut counter = branch_container.first_child(); while let Some(row) = counter { if row.widget_name() == "AdwActionRow" { if !searchbar.text().is_empty() { if row.property::("subtitle").to_lowercase().contains(&searchbar.text().to_string().to_lowercase()) || row.property::("title").to_lowercase().contains(&searchbar.text().to_string().to_lowercase()) { //row.grab_focus(); //row.add_css_class("highlight-widget"); row.set_property("visible", true); searchbar.grab_focus(); } else { row.set_property("visible", false); } } else { row.set_property("visible", true); } } counter = row.next_sibling(); } })); boxedlist } pub fn create_kernel_badge( label0_text: &str, label1_text: &str, css_style: &str, group_size: >k::SizeGroup, group_size0: >k::SizeGroup, group_size1: >k::SizeGroup, ) -> gtk::ListBox { let badge_box = gtk::Box::builder().build(); let label0 = gtk::Label::builder() .label(label0_text) .margin_start(5) .margin_end(5) .margin_bottom(1) .margin_top(1) .hexpand(true) .vexpand(true) .build(); group_size0.add_widget(&label0); let label_seprator = gtk::Separator::builder().build(); let label1 = gtk::Label::builder() .label(label1_text) .margin_start(3) .margin_end(0) .margin_bottom(1) .margin_top(1) .hexpand(true) .vexpand(true) .build(); group_size1.add_widget(&label1); label1.add_css_class(css_style); badge_box.append(&label0); badge_box.append(&label_seprator); badge_box.append(&label1); let boxedlist = gtk::ListBox::builder() .selection_mode(SelectionMode::None) .halign(Align::Center) .valign(Align::End) .margin_start(5) .margin_end(5) .margin_bottom(5) .margin_top(5) .build(); boxedlist.add_css_class("boxed-list"); boxedlist.append(&badge_box); group_size.add_widget(&boxedlist); boxedlist } fn get_kernel_branches() -> Vec { let mut kernel_branches_array: Vec = Vec::new(); let data = fs::read_to_string( "/home/ward/RustroverProjects/fedora-kernel-manager/data/kernel_branches.json", ) .expect("Unable to read file"); let res: serde_json::Value = serde_json::from_str(&data).expect("Unable to parse"); if let serde_json::Value::Array(branches) = &res["branches"] { for branch in branches { println!( "Downloading & Parsing package DB for {}.", branch["name"].as_str().to_owned().unwrap().to_string() ); let branch = KernelBranch { name: branch["name"].as_str().to_owned().unwrap().to_string(), db_url: branch["db_url"].as_str().to_owned().unwrap().to_string(), db: reqwest::blocking::get( branch["db_url"].as_str().to_owned().unwrap().to_string(), ) .unwrap() .text() .unwrap(), }; kernel_branches_array.push(branch) } }; kernel_branches_array } pub fn get_running_kernel_info() -> RunningKernelInfo { let kernel = match Command::new("uname") .arg("-r") .stdout(Stdio::piped()) .output() { Ok(t) => String::from_utf8(t.stdout).unwrap().trim().to_owned(), Err(_) => "Unknown".to_string(), }; let version = match linux_version::linux_kernel_version() { Ok(t) => { if t.patch == 0 { format!("{}.{}", t.major, t.minor) } else { format!("{}.{}.{}", t.major, t.minor, t.patch) } } Err(_) => "Unknown".to_string(), }; let info = RunningKernelInfo { kernel: kernel, version: version.clone(), // didn't find a way to accurately get this, outside of sched-ext (https://github.com/CachyOS/kernel-manager/blob/develop/src/schedext-window.cpp) sched: get_current_scheduler(version), }; info } pub fn get_current_scheduler(version: String) -> String { if Path::new("/sys/kernel/sched_ext/root/ops").exists() { println!("sched_ext is detected, getting scx scheduler"); let scx_sched = match fs::read_to_string("/sys/kernel/sched_ext/root/ops") { Ok(t) => t, Err(_) => "unknown!".to_string(), }; "sched_ext: ".to_owned() + &scx_sched } else if bore_check() { "BORE".to_string() } else if Version::from(&version) >= Version::from("6.6") { "EEVDF?".to_string() } else { "CFS?".to_string() } } fn bore_check() -> bool { let is_bore = match cmd!("sysctl", "-n", "kernel.sched_bore").read() { Ok(t) => { if t == "1" { true } else { false } } Err(_) => false, }; is_bore } fn create_kernel_badges(badge_box: >k::Box, running_kernel_info: &RunningKernelInfo) { let kernel_badges_size_group = gtk::SizeGroup::new(SizeGroupMode::Both); let kernel_badges_size_group0 = gtk::SizeGroup::new(SizeGroupMode::Both); let kernel_badges_size_group1 = gtk::SizeGroup::new(SizeGroupMode::Both); let kernel_version = "6.9"; let version_css_style = if &running_kernel_info.version.as_str() == &kernel_version { "background-green-bg" } else { "background-red-bg" }; while let Some(widget) = badge_box.last_child() { badge_box.remove(&widget); } badge_box.append(&create_kernel_badge( "Kernel Branch", "cachy", "background-accent-bg", &kernel_badges_size_group, &kernel_badges_size_group0, &kernel_badges_size_group1, )); badge_box.append(&create_kernel_badge( "Latest Version", "6.9", "background-accent-bg", &kernel_badges_size_group, &kernel_badges_size_group0, &kernel_badges_size_group1, )); badge_box.append(&create_kernel_badge( "Running Version", &running_kernel_info.version, &version_css_style, &kernel_badges_size_group, &kernel_badges_size_group0, &kernel_badges_size_group1, )); badge_box.append(&create_kernel_badge( "Running Kernel", &running_kernel_info.kernel, &version_css_style, &kernel_badges_size_group, &kernel_badges_size_group0, &kernel_badges_size_group1, )); badge_box.append(&create_kernel_badge( "Running Sched", &running_kernel_info.sched, "background-accent-bg", &kernel_badges_size_group, &kernel_badges_size_group0, &kernel_badges_size_group1, )); } fn save_branch_config(branch: &str) { let config_path = get_my_home() .unwrap() .unwrap() .join(".config/fedora-kernel-manager"); match &config_path.exists() { true => fs::write(config_path.join("branch"), branch).unwrap(), _ => { fs::create_dir(&config_path).unwrap(); fs::write(config_path.join("branch"), branch).unwrap(); } } }