diff --git a/Cargo.lock b/Cargo.lock index 1e2a55d..c848e2d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -326,6 +326,7 @@ dependencies = [ "homedir", "libadwaita", "linux-version", + "os_pipe", "reqwest", "serde_json", "version-compare", diff --git a/Cargo.toml b/Cargo.toml index 127cfe8..945ce64 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,7 @@ glib = "0.19.7" gtk = { version = "0.8.2", package = "gtk4", features = ["v4_14"] } homedir = "0.2.1" linux-version = "0.1.1" +os_pipe = "1.2.0" reqwest = { version = "0.11", features = ["blocking"] } serde_json = "1.0.117" version-compare = "0.2.0" diff --git a/src/build_ui/mod.rs b/src/build_ui/mod.rs index cd73992..8f40c19 100644 --- a/src/build_ui/mod.rs +++ b/src/build_ui/mod.rs @@ -68,8 +68,16 @@ pub fn build_ui(app: &adw::Application) { let window_toolbar = adw::ToolbarView::builder().content(&content_stack).build(); + let window = adw::ApplicationWindow::builder() + .application(app) + .content(&window_toolbar) + .width_request(600) + .height_request(600) + .resizable(false) + .build(); + content_stack.add_named( - &content::content(&content_stack, &selected_kernel_branch2, &db_load_complete), + &content::content(&content_stack, &selected_kernel_branch2, &db_load_complete, &window), Some("content_page"), ); content_stack.add_named( @@ -80,14 +88,6 @@ pub fn build_ui(app: &adw::Application) { window_toolbar.add_top_bar(&window_headerbar); window_toolbar.add_top_bar(&window_banner); - let window = adw::ApplicationWindow::builder() - .application(app) - .content(&window_toolbar) - .width_request(600) - .height_request(600) - .resizable(false) - .build(); - load_icon_theme(&window); window.connect_close_request(move |window| { diff --git a/src/content/mod.rs b/src/content/mod.rs index f106cd5..dd4c9f2 100644 --- a/src/content/mod.rs +++ b/src/content/mod.rs @@ -20,6 +20,7 @@ pub fn content( content_stack: >k::Stack, selected_kernel_branch: &Rc>, db_load_complete: &Rc>, + window: &adw::ApplicationWindow, ) -> gtk::Box { let running_kernel_info = get_running_kernel_info(); @@ -98,7 +99,6 @@ pub fn content( 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) @@ -128,9 +128,9 @@ pub fn content( .build(); browse_kernels_button.add_css_class("circular"); - browse_kernels_button.connect_clicked(clone!(@weak content_stack, @strong selected_kernel_branch => move |_| { + browse_kernels_button.connect_clicked(clone!(@weak window, @weak content_stack, @strong selected_kernel_branch => move |_| { content_stack.add_named( - &kernel_pkg::kernel_pkg_page(&content_stack, &selected_kernel_branch), + &kernel_pkg::kernel_pkg_page(&content_stack, &window, &selected_kernel_branch), Some("kernel_pkg_page"), ); content_stack.set_visible_child_name("kernel_pkg_page") diff --git a/src/kernel_pkg/mod.rs b/src/kernel_pkg/mod.rs index 627ebda..53bd3f2 100644 --- a/src/kernel_pkg/mod.rs +++ b/src/kernel_pkg/mod.rs @@ -1,3 +1,6 @@ +use std::io::BufReader; +use duct::cmd; +use std::process::Command; use std::cell::RefCell; use crate::content::get_running_kernel_info; use crate::{KernelBranch, RunningKernelInfo}; @@ -8,8 +11,11 @@ use gtk::*; use std::fs; use std::fs::*; use std::rc::Rc; +use std::time::*; +use std::error::Error; +use std::io::BufRead; -pub fn kernel_pkg_page(content_stack: >k::Stack, selected_kernel_branch: &Rc>) -> gtk::Box { +pub fn kernel_pkg_page(content_stack: >k::Stack, window: &adw::ApplicationWindow, selected_kernel_branch: &Rc>) -> gtk::Box { let selected_kernel_branch_clone0 = selected_kernel_branch.borrow().clone(); let main_box = gtk::Box::builder() @@ -42,7 +48,8 @@ pub fn kernel_pkg_page(content_stack: >k::Stack, selected_kernel_branch: &Rc = log_loop_sender.clone(); + + let (log_status_loop_sender, log_status_loop_receiver) = async_channel::unbounded(); + let log_status_loop_sender: async_channel::Sender = + log_status_loop_sender.clone(); + + let (kernel_status_loop_sender, kernel_status_loop_receiver) = async_channel::unbounded(); + let kernel_status_loop_sender: async_channel::Sender = + kernel_status_loop_sender.clone(); + + std::thread::spawn(move || loop { + // let command_installed_status = std::Command::new("dpkg") + // .args(["-s", &kernel_package_ind2]) + // .output() + // .unwrap(); + //if command_installed_status.status.success() { + // kernel_status_loop_sender.send_blocking(true).expect("channel needs to be open") + //} else { + // kernel_status_loop_sender.send_blocking(false).expect("channel needs to be open") + //} + std::thread::sleep(Duration::from_secs(6)); + kernel_status_loop_sender.send_blocking(true).expect("channel needs to be open") + }); + + let kernel_expander_row = adw::ExpanderRow::new(); + let kernel_package_label = gtk::Label::builder() + .label(&kernel_package) + .build(); + let kernel_status_icon = gtk::Image::builder() + .icon_name("emblem-default") + .pixel_size(24) + .visible(false) + .tooltip_text("Installed") + .build(); + let kernel_description_label = gtk::Label::builder() + .label(&kernel_description) + .build(); + let kernel_content_row = adw::ActionRow::builder().build(); + let kernel_install_button = gtk::Button::builder() + .margin_start(5) + .margin_top(5) + .margin_bottom(5) + .valign(gtk::Align::Center) + .label("Install") + .tooltip_text("Install this kernel") + .sensitive(false) + .build(); + kernel_install_button.add_css_class("suggested-action"); + let kernel_remove_button = gtk::Button::builder() + .margin_end(5) + .margin_top(5) + .margin_bottom(5) + .valign(gtk::Align::Center) + .label("Uninstall") + .tooltip_text("Uninstall this kernel") + .sensitive(false) + .build(); + let kernel_action_box = gtk::Box::builder().homogeneous(true).build(); + kernel_remove_button.add_css_class("destructive-action"); + kernel_expander_row.add_prefix(&kernel_package_label); + kernel_expander_row.add_suffix(&kernel_status_icon); + kernel_expander_row.set_title(&kernel_name); + //kernel_expander_row.set_subtitle(&kernel.clone().version); + // + kernel_content_row.add_prefix(&kernel_description_label); + kernel_action_box.append(&kernel_remove_button); + kernel_action_box.append(&kernel_install_button); + kernel_content_row.add_suffix(&kernel_action_box); + kernel_expander_row.add_row(&kernel_content_row); + rows_size_group.add_widget(&kernel_action_box); + // + let kernel_status_loop_context = MainContext::default(); + // The main loop executes the asynchronous block + kernel_status_loop_context.spawn_local(clone!(@weak kernel_remove_button, @weak kernel_install_button, @strong kernel_status_loop_receiver => async move { + while let Ok(kernel_status_state) = kernel_status_loop_receiver.recv().await { + if kernel_status_state == true { + kernel_status_icon.set_visible(true); + kernel_install_button.set_sensitive(false); + kernel_remove_button.set_sensitive(true); + } else { + kernel_status_icon.set_visible(false); + kernel_remove_button.set_sensitive(false); + kernel_install_button.set_sensitive(true); + } + } + })); + // + let kernel_install_log_terminal_buffer = gtk::TextBuffer::builder().build(); + + let kernel_install_log_terminal = gtk::TextView::builder() + .vexpand(true) + .hexpand(true) + .editable(false) + .buffer(&kernel_install_log_terminal_buffer) + .build(); + + let kernel_install_log_terminal_scroll = gtk::ScrolledWindow::builder() + .width_request(400) + .height_request(200) + .vexpand(true) + .hexpand(true) + .child(&kernel_install_log_terminal) + .build(); + + let kernel_install_dialog = adw::MessageDialog::builder() + .transient_for(window) + .hide_on_close(true) + .extra_child(&kernel_install_log_terminal_scroll) + .width_request(400) + .height_request(200) + .heading("Installing Kernel") + .build(); + kernel_install_dialog.add_response( + "kernel_install_dialog_ok", + "OK", + ); + kernel_install_dialog.add_response( + "kernel_install_dialog_reboot", + "Reboot Now (Optional)", + ); + kernel_install_dialog.set_response_appearance( + "kernel_install_dialog_reboot", + adw::ResponseAppearance::Suggested, + ); + // + + // + let log_loop_context = MainContext::default(); + // The main loop executes the asynchronous block + log_loop_context.spawn_local(clone!(@weak kernel_install_log_terminal_buffer, @weak kernel_install_dialog, @strong log_loop_receiver => async move { + while let Ok(state) = log_loop_receiver.recv().await { + kernel_install_log_terminal_buffer.insert(&mut kernel_install_log_terminal_buffer.end_iter(), &("\n".to_string() + &state)) + } + })); + + let log_status_loop_context = MainContext::default(); + // The main loop executes the asynchronous block + log_status_loop_context.spawn_local(clone!(@weak kernel_install_dialog, @strong log_status_loop_receiver => async move { + while let Ok(state) = log_status_loop_receiver.recv().await { + if state == true { + kernel_install_dialog.set_response_enabled("kernel_install_dialog_ok", true); + //if get_current_username().unwrap() == "pikaos" { + // kernel_install_dialog.set_response_enabled("kernel_install_dialog_reboot", false); + //} else { + // kernel_install_dialog.set_response_enabled("kernel_install_dialog_reboot", true); + //} + kernel_install_dialog.set_response_enabled("kernel_install_dialog_reboot", true); + kernel_install_dialog.set_body("Kernel installation was successful!"); + } else { + kernel_install_dialog.set_response_enabled("kernel_install_dialog_ok", true); + kernel_install_dialog.set_body("Kernel Installation Failed!"); + kernel_install_dialog.set_response_enabled("kernel_install_dialog_reboot", false); + } + } + })); + // + kernel_install_log_terminal_buffer.connect_changed(clone!(@weak kernel_install_log_terminal, @weak kernel_install_log_terminal_buffer,@weak kernel_install_log_terminal_scroll => move |_|{ + if kernel_install_log_terminal_scroll.vadjustment().upper() - kernel_install_log_terminal_scroll.vadjustment().value() > 100.0 { + kernel_install_log_terminal_scroll.vadjustment().set_value(kernel_install_log_terminal_scroll.vadjustment().upper()) + } + })); + // + kernel_install_button.connect_clicked(clone!(@weak kernel_install_log_terminal,@weak kernel_install_log_terminal_buffer, @weak kernel_install_dialog, @strong log_loop_sender, @strong log_status_loop_sender, @strong kernel_package => move |_| { + kernel_install_log_terminal_buffer.delete(&mut kernel_install_log_terminal_buffer.bounds().0, &mut kernel_install_log_terminal_buffer.bounds().1); + kernel_install_dialog.set_response_enabled("kernel_install_dialog_ok", false); + kernel_install_dialog.set_response_enabled("kernel_install_dialog_reboot", false); + kernel_install_dialog.set_body(""); + kernel_install_dialog.choose(None::<&gio::Cancellable>, move |choice| { + if choice == "kernel_install_dialog_reboot" { + Command::new("systemctl") + .arg("reboot") + .spawn() + .expect("systemctl reboot failed to start"); + } + }); + let log_status_loop_sender_clone = log_status_loop_sender.clone(); + let log_loop_sender_clone= log_loop_sender.clone(); + let kernel_package_clone = kernel_package.clone(); + std::thread::spawn(move || { + let command = kernel_modify(log_loop_sender_clone, &kernel_package_clone); + match command { + Ok(_) => { + println!("Status: kernel modify Successful"); + log_status_loop_sender_clone.send_blocking(true).expect("The channel needs to be open."); + } + Err(_) => { + println!("Status: kernel modify Failed"); + log_status_loop_sender_clone.send_blocking(false).expect("The channel needs to be open."); + } + } + }); + })); + kernel_remove_button.connect_clicked(clone!(@weak kernel_install_log_terminal,@weak kernel_install_log_terminal_buffer, @weak kernel_install_dialog, @strong log_loop_sender, @strong log_status_loop_sender, @strong kernel_package => move |_| { + kernel_install_log_terminal_buffer.delete(&mut kernel_install_log_terminal_buffer.bounds().0, &mut kernel_install_log_terminal_buffer.bounds().1); + kernel_install_dialog.set_response_enabled("kernel_install_dialog_ok", false); + kernel_install_dialog.set_response_enabled("kernel_install_dialog_reboot", false); + kernel_install_dialog.set_body(""); + kernel_install_dialog.choose(None::<&gio::Cancellable>, move |choice| { + if choice == "kernel_install_dialog_reboot" { + Command::new("systemctl") + .arg("reboot") + .spawn() + .expect("systemctl reboot failed to start"); + } + }); + let log_status_loop_sender_clone = log_status_loop_sender.clone(); + let log_loop_sender_clone= log_loop_sender.clone(); + let kernel_package_clone = kernel_package.clone(); + std::thread::spawn(move || { + let command = kernel_modify(log_loop_sender_clone, &kernel_package_clone); + match command { + Ok(_) => { + println!("Status: kernel modify Successful"); + log_status_loop_sender_clone.send_blocking(true).expect("The channel needs to be open."); + } + Err(_) => { + println!("Status: kernel modify Failed"); + log_status_loop_sender_clone.send_blocking(false).expect("The channel needs to be open."); + } + } + }); + })); + // + boxedlist.append(&kernel_expander_row); + }}; +} + +const KERNEL_MODIFY_PROG: &str = r###" +#! /bin/bash +DRIVER="$0" +/usr/lib/pika/drivers/modify-driver.sh "${DRIVER}" +"###; +fn kernel_modify( + log_loop_sender: async_channel::Sender, + kernel_pkg: &str, +) -> Result<(), std::boxed::Box> { + let (pipe_reader, pipe_writer) = os_pipe::pipe()?; + let child = cmd!("bash", "-c", KERNEL_MODIFY_PROG, kernel_pkg) + .stderr_to_stdout() + .stdout_file(pipe_writer) + .start()?; + for line in BufReader::new(pipe_reader).lines() { + log_loop_sender + .send_blocking(line?) + .expect("Channel needs to be opened.") + } + child.wait()?; + + Ok(()) +} \ No newline at end of file