787 lines
27 KiB
Rust
787 lines
27 KiB
Rust
#![feature(const_slice_from_raw_parts)]
|
||
#![feature(const_str_from_utf8)]
|
||
#![allow(non_upper_case_globals)]
|
||
|
||
mod config;
|
||
mod data_types;
|
||
mod utils;
|
||
|
||
use config::{APP_ID, GETTEXT_PACKAGE, LOCALEDIR, PKGDATADIR, PROFILE, VERSION};
|
||
use data_types::*;
|
||
use gettextrs::LocaleCategory;
|
||
use gtk::{gio, glib, Builder, HeaderBar, Window};
|
||
use once_cell::sync::Lazy;
|
||
use std::collections::HashMap;
|
||
use std::fmt::Write as _;
|
||
use std::path::Path;
|
||
use std::sync::Mutex;
|
||
use utils::*;
|
||
|
||
use gio::prelude::*;
|
||
use gtk::prelude::*;
|
||
|
||
use gdk_pixbuf::Pixbuf;
|
||
|
||
use serde_json::json;
|
||
use std::sync::Arc;
|
||
use std::{fs, str};
|
||
use subprocess::{Exec, Redirection};
|
||
|
||
static mut g_save_json: Lazy<Mutex<serde_json::Value>> = Lazy::new(|| Mutex::new(json!(null)));
|
||
static mut g_local_units: Lazy<Mutex<SystemdUnits>> = Lazy::new(|| Mutex::new(SystemdUnits::new()));
|
||
static mut g_global_units: Lazy<Mutex<SystemdUnits>> =
|
||
Lazy::new(|| Mutex::new(SystemdUnits::new()));
|
||
|
||
static mut g_hello_window: Option<Arc<HelloWindow>> = None;
|
||
|
||
fn quick_message(message: &str) {
|
||
// Create the widgets
|
||
let dialog = gtk::Dialog::builder().title(message).modal(true).build();
|
||
|
||
dialog.set_destroy_with_parent(true);
|
||
dialog.add_button("_Offline", gtk::ResponseType::No);
|
||
dialog.add_button("_Online", gtk::ResponseType::Yes);
|
||
let content_area = dialog.content_area();
|
||
let label = gtk::Label::new(Some(message));
|
||
|
||
// Add the label, and show everything we’ve added
|
||
content_area.add(&label);
|
||
dialog.show_all();
|
||
|
||
let result = dialog.run();
|
||
let cmd: String;
|
||
if result == gtk::ResponseType::No {
|
||
cmd = fix_path("/usr/local/bin/calamares-offline.sh");
|
||
} else if result == gtk::ResponseType::Yes {
|
||
cmd = fix_path("/usr/local/bin/calamares-online.sh");
|
||
} else {
|
||
unsafe {
|
||
dialog.destroy();
|
||
}
|
||
return;
|
||
}
|
||
|
||
// Spawn child process in separate thread.
|
||
std::thread::spawn(move || {
|
||
Exec::shell(cmd).join().unwrap();
|
||
});
|
||
|
||
unsafe {
|
||
dialog.destroy();
|
||
}
|
||
}
|
||
|
||
fn create_options_section() -> gtk::Box {
|
||
let topbox = gtk::Box::new(gtk::Orientation::Vertical, 2);
|
||
let box_collection = gtk::Box::new(gtk::Orientation::Horizontal, 10);
|
||
let label = gtk::Label::new(None);
|
||
label.set_line_wrap(true);
|
||
label.set_justify(gtk::Justification::Center);
|
||
label.set_text("Tweaks");
|
||
|
||
let psd_btn = gtk::CheckButton::with_label("Profile-sync-daemon enable");
|
||
let systemd_oomd_btn = gtk::CheckButton::with_label("Systemd-oomd enabled");
|
||
let apparmor_btn = gtk::CheckButton::with_label("Apparmor enabled");
|
||
let ananicy_cpp_btn = gtk::CheckButton::with_label("Ananicy Cpp enabled");
|
||
|
||
unsafe {
|
||
psd_btn.set_data("actionData", "psd.service");
|
||
psd_btn.set_data("actionType", "user_service");
|
||
systemd_oomd_btn.set_data("actionData", "systemd-oomd.service");
|
||
systemd_oomd_btn.set_data("actionType", "service");
|
||
apparmor_btn.set_data("actionData", "apparmor.service");
|
||
apparmor_btn.set_data("actionType", "service");
|
||
ananicy_cpp_btn.set_data("actionData", "ananicy-cpp.service");
|
||
ananicy_cpp_btn.set_data("actionType", "service");
|
||
}
|
||
|
||
for btn in &[&psd_btn, &systemd_oomd_btn, &apparmor_btn, &ananicy_cpp_btn] {
|
||
unsafe {
|
||
let data: &str = *btn.data("actionData").unwrap().as_ptr();
|
||
if g_local_units.lock().unwrap().enabled_units.contains(&String::from(data))
|
||
|| g_global_units.lock().unwrap().enabled_units.contains(&String::from(data))
|
||
{
|
||
btn.set_active(true);
|
||
}
|
||
}
|
||
}
|
||
|
||
psd_btn.connect_clicked(on_servbtn_clicked);
|
||
systemd_oomd_btn.connect_clicked(on_servbtn_clicked);
|
||
apparmor_btn.connect_clicked(on_servbtn_clicked);
|
||
ananicy_cpp_btn.connect_clicked(on_servbtn_clicked);
|
||
|
||
topbox.pack_start(&label, true, false, 1);
|
||
box_collection.pack_start(&psd_btn, true, false, 2);
|
||
box_collection.pack_start(&systemd_oomd_btn, true, false, 2);
|
||
box_collection.pack_start(&apparmor_btn, true, false, 2);
|
||
box_collection.pack_start(&ananicy_cpp_btn, true, false, 2);
|
||
box_collection.set_halign(gtk::Align::Fill);
|
||
topbox.pack_start(&box_collection, true, false, 1);
|
||
|
||
topbox.set_hexpand(true);
|
||
topbox
|
||
}
|
||
|
||
fn create_apps_section() -> gtk::Box {
|
||
let topbox = gtk::Box::new(gtk::Orientation::Vertical, 2);
|
||
let box_collection = gtk::Box::new(gtk::Orientation::Horizontal, 10);
|
||
let label = gtk::Label::new(None);
|
||
label.set_line_wrap(true);
|
||
label.set_justify(gtk::Justification::Center);
|
||
label.set_text("Applications");
|
||
|
||
let cachyos_pi = gtk::Button::with_label("CachyOS PackageInstaller");
|
||
let cachyos_km = gtk::Button::with_label("CachyOS Kernel Manager");
|
||
|
||
cachyos_pi.connect_clicked(on_appbtn_clicked);
|
||
cachyos_km.connect_clicked(on_appbtn_clicked);
|
||
|
||
box_collection.pack_start(&cachyos_pi, true, true, 2);
|
||
box_collection.pack_start(&cachyos_km, true, true, 2);
|
||
|
||
topbox.pack_start(&label, true, true, 2);
|
||
|
||
box_collection.set_halign(gtk::Align::Fill);
|
||
topbox.pack_start(&box_collection, true, true, 0);
|
||
|
||
topbox.set_hexpand(true);
|
||
topbox
|
||
}
|
||
|
||
fn show_about_dialog() {
|
||
let main_window: Window;
|
||
unsafe {
|
||
main_window = g_hello_window.clone().unwrap().window.clone();
|
||
}
|
||
let dialog = gtk::AboutDialog::builder()
|
||
.transient_for(&main_window)
|
||
.modal(true)
|
||
.program_name(&gettextrs::gettext("CachyOS Hello"))
|
||
.comments(&gettextrs::gettext("Welcome screen for CachyOS"))
|
||
.version(VERSION)
|
||
.logo_icon_name(APP_ID)
|
||
.authors(vec![
|
||
"Vladislav Nepogodin".into(),
|
||
])
|
||
// Translators: Replace "translator-credits" with your names. Put a comma between.
|
||
.translator_credits(&gettextrs::gettext("translator-credits"))
|
||
.copyright("2021-2022 CachyOS team")
|
||
.license_type(gtk::License::Gpl30)
|
||
.website("https://github.com/cachyos/cachyos-welcome")
|
||
.website_label("GitHub")
|
||
.build();
|
||
|
||
dialog.run();
|
||
dialog.hide();
|
||
}
|
||
|
||
fn main() {
|
||
gettextrs::setlocale(LocaleCategory::LcAll, "");
|
||
gettextrs::bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR).expect("Unable to bind the text domain.");
|
||
gettextrs::textdomain(GETTEXT_PACKAGE).expect("Unable to switch to the text domain.");
|
||
|
||
glib::set_application_name("CachyOSHello");
|
||
|
||
gtk::init().expect("Unable to start GTK3.");
|
||
|
||
let application = gtk::Application::new(
|
||
Some(APP_ID), // Application id
|
||
Default::default(), // Using default flags
|
||
);
|
||
|
||
application.connect_activate(|application| {
|
||
build_ui(application);
|
||
});
|
||
|
||
// Run the application and start the event loop
|
||
application.run();
|
||
}
|
||
|
||
fn build_ui(application: >k::Application) {
|
||
let data = fs::read_to_string(format!("{}/data/preferences.json", PKGDATADIR))
|
||
.expect("Unable to read file");
|
||
let preferences: serde_json::Value = serde_json::from_str(&data).expect("Unable to parse");
|
||
|
||
// Get saved infos
|
||
let save_path = fix_path(preferences["save_path"].as_str().unwrap());
|
||
let save: serde_json::Value = if !Path::new(&save_path).exists() {
|
||
json!({"locale": ""})
|
||
} else {
|
||
read_json(save_path.as_str())
|
||
};
|
||
|
||
// Import Css
|
||
let provider = gtk::CssProvider::new();
|
||
provider.load_from_path(preferences["style_path"].as_str().unwrap()).unwrap();
|
||
if let Some(screen) = gdk::Screen::default() {
|
||
gtk::StyleContext::add_provider_for_screen(
|
||
&screen,
|
||
&provider,
|
||
gtk::STYLE_PROVIDER_PRIORITY_APPLICATION,
|
||
);
|
||
}
|
||
|
||
// Init window
|
||
let builder: Builder = Builder::from_file(preferences["ui_path"].as_str().unwrap());
|
||
builder.connect_signals(|_builder, handler_name| {
|
||
match handler_name {
|
||
// handler_name as defined in the glade file => handler function as defined above
|
||
"on_languages_changed" => Box::new(on_languages_changed),
|
||
"on_action_clicked" => Box::new(on_action_clicked),
|
||
"on_btn_clicked" => Box::new(on_btn_clicked),
|
||
"on_link_clicked" => Box::new(on_link_clicked),
|
||
"on_delete_window" => Box::new(on_delete_window),
|
||
_ => Box::new(|_| None),
|
||
}
|
||
});
|
||
|
||
let main_window: Window = builder.object("window").expect("Could not get the object window");
|
||
main_window.set_application(Some(application));
|
||
|
||
unsafe {
|
||
g_hello_window = Some(Arc::new(HelloWindow {
|
||
window: main_window.clone(),
|
||
builder: builder.clone(),
|
||
preferences: preferences.clone(),
|
||
}));
|
||
|
||
*g_save_json.lock().unwrap() = save.clone();
|
||
};
|
||
|
||
// Subtitle of headerbar
|
||
let header: HeaderBar = builder.object("headerbar").expect("Could not get the headerbar");
|
||
|
||
header.set_subtitle(Some("CachyOS rolling"));
|
||
|
||
// Load images
|
||
let logo_path = preferences["logo_path"].as_str().unwrap();
|
||
if Path::new(&logo_path).exists() {
|
||
let logo = Pixbuf::from_file(logo_path).unwrap();
|
||
main_window.set_icon(Some(&logo));
|
||
|
||
let image: gtk::Image = builder.object("distriblogo").unwrap();
|
||
image.set_from_pixbuf(Some(&logo));
|
||
|
||
let dialog: gtk::AboutDialog = builder.object("aboutdialog").unwrap();
|
||
dialog.set_logo(Some(&logo));
|
||
}
|
||
|
||
let social_box: gtk::Box = builder.object("social").unwrap();
|
||
for btn in social_box.children() {
|
||
let name = btn.widget_name();
|
||
let icon_path = format!("{}/data/img/{}.png", PKGDATADIR, name);
|
||
let image: gtk::Image = builder.object(name.as_str()).unwrap();
|
||
image.set_from_file(Some(&icon_path));
|
||
}
|
||
|
||
let homepage_grid: gtk::Grid = builder.object("homepage").unwrap();
|
||
for widget in homepage_grid.children() {
|
||
let casted_widget = widget.downcast::<gtk::Button>();
|
||
if casted_widget.is_err() {
|
||
continue;
|
||
}
|
||
|
||
let btn = casted_widget.unwrap();
|
||
if btn.image_position() != gtk::PositionType::Right {
|
||
continue;
|
||
}
|
||
let image_path = format!("{}/data/img/external-link.png", PKGDATADIR);
|
||
let image = gtk::Image::new();
|
||
image.set_from_file(Some(&image_path));
|
||
image.set_margin_start(2);
|
||
btn.set_image(Some(&image));
|
||
}
|
||
|
||
// Create pages
|
||
let pages =
|
||
format!("{}/data/pages/{}", PKGDATADIR, preferences["default_locale"].as_str().unwrap());
|
||
|
||
for page in fs::read_dir(pages).unwrap() {
|
||
let scrolled_window =
|
||
gtk::ScrolledWindow::new(gtk::Adjustment::NONE, gtk::Adjustment::NONE);
|
||
|
||
let viewport = gtk::Viewport::new(gtk::Adjustment::NONE, gtk::Adjustment::NONE);
|
||
viewport.set_border_width(10);
|
||
|
||
let label = gtk::Label::new(None);
|
||
label.set_line_wrap(true);
|
||
let image = gtk::Image::from_icon_name(Some("go-previous"), gtk::IconSize::Button);
|
||
let back_btn = gtk::Button::new();
|
||
back_btn.set_image(Some(&image));
|
||
back_btn.set_widget_name("home");
|
||
|
||
back_btn.connect_clicked(glib::clone!(@weak builder => move |button| {
|
||
let name = button.widget_name();
|
||
let stack: gtk::Stack = builder.object("stack").unwrap();
|
||
stack.set_visible_child_name(&format!("{}page", name));
|
||
}));
|
||
|
||
let grid = gtk::Grid::new();
|
||
grid.attach(&back_btn, 0, 1, 1, 1);
|
||
grid.attach(&label, 1, 2, 1, 1);
|
||
viewport.add(&grid);
|
||
scrolled_window.add(&viewport);
|
||
scrolled_window.show_all();
|
||
|
||
let stack: gtk::Stack = builder.object("stack").unwrap();
|
||
let child_name =
|
||
format!("{}page", page.unwrap().path().file_name().unwrap().to_str().unwrap());
|
||
stack.add_named(&scrolled_window, &child_name);
|
||
}
|
||
|
||
// Init translation
|
||
gettextrs::bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR)
|
||
.expect("Unable to switch to the text domain.");
|
||
gettextrs::bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8")
|
||
.expect("Unable to set domain encoding.");
|
||
gettextrs::textdomain(GETTEXT_PACKAGE).expect("Unable to switch to the text domain.");
|
||
let languages: gtk::ComboBoxText = builder.object("languages").unwrap();
|
||
languages.set_active_id(Some(get_best_locale(&preferences, &save).as_str()));
|
||
|
||
// Set autostart switcher state
|
||
let autostart = Path::new(&fix_path(preferences["autostart_path"].as_str().unwrap())).exists();
|
||
let autostart_switch: gtk::Switch = builder.object("autostart").unwrap();
|
||
autostart_switch.set_active(autostart);
|
||
|
||
// Live systems
|
||
if (Path::new(&preferences["live_path"].as_str().unwrap()).exists())
|
||
&& (check_regular_file(preferences["installer_path"].as_str().unwrap()))
|
||
{
|
||
let installlabel: gtk::Label = builder.object("installlabel").unwrap();
|
||
installlabel.set_visible(true);
|
||
|
||
let install: gtk::Button = builder.object("install").unwrap();
|
||
install.set_visible(true);
|
||
|
||
// Show the UI
|
||
main_window.show();
|
||
return;
|
||
} else {
|
||
let installlabel: gtk::Label = builder.object("installlabel").unwrap();
|
||
installlabel.set_visible(false);
|
||
|
||
let install: gtk::Button = builder.object("install").unwrap();
|
||
install.set_visible(false);
|
||
}
|
||
let install: gtk::Button = builder.object("appBrowser").unwrap();
|
||
install.set_visible(true);
|
||
|
||
load_enabled_units();
|
||
load_global_enabled_units();
|
||
|
||
let viewport = gtk::Viewport::new(gtk::Adjustment::NONE, gtk::Adjustment::NONE);
|
||
// let label = gtk::Label::new(None);
|
||
// label.set_line_wrap(true);
|
||
let image = gtk::Image::from_icon_name(Some("go-previous"), gtk::IconSize::Button);
|
||
let back_btn = gtk::Button::new();
|
||
back_btn.set_image(Some(&image));
|
||
back_btn.set_widget_name("home");
|
||
|
||
back_btn.connect_clicked(glib::clone!(@weak builder => move |button| {
|
||
let name = button.widget_name();
|
||
let stack: gtk::Stack = builder.object("stack").unwrap();
|
||
stack.set_visible_child_name(&format!("{}page", name));
|
||
}));
|
||
|
||
let options_section_box = create_options_section();
|
||
let apps_section_box = create_apps_section();
|
||
|
||
let grid = gtk::Grid::new();
|
||
grid.set_hexpand(true);
|
||
grid.set_margin_start(10);
|
||
grid.set_margin_end(10);
|
||
grid.set_margin_top(5);
|
||
grid.set_margin_bottom(5);
|
||
grid.attach(&back_btn, 0, 1, 1, 1);
|
||
let box_collection = gtk::Box::new(gtk::Orientation::Vertical, 5);
|
||
|
||
box_collection.pack_start(&options_section_box, true, true, 5);
|
||
box_collection.pack_start(&apps_section_box, true, true, 5);
|
||
|
||
box_collection.set_valign(gtk::Align::Center);
|
||
box_collection.set_halign(gtk::Align::Center);
|
||
grid.attach(&box_collection, 1, 2, 5, 1);
|
||
viewport.add(&grid);
|
||
viewport.show_all();
|
||
|
||
let stack: gtk::Stack = builder.object("stack").unwrap();
|
||
let child_name = "appBrowserpage";
|
||
stack.add_named(&viewport, child_name);
|
||
|
||
// Show the UI
|
||
main_window.show();
|
||
}
|
||
|
||
/// Returns the best locale, based on user's preferences.
|
||
pub fn get_best_locale(preferences: &serde_json::Value, save: &serde_json::Value) -> String {
|
||
let saved_locale =
|
||
format!("{}/{}/LC_MESSAGES/cachyos-hello.mo", LOCALEDIR, save["locale"].as_str().unwrap());
|
||
if check_regular_file(saved_locale.as_str()) {
|
||
return String::from(save["locale"].as_str().unwrap());
|
||
} else if save["locale"].as_str().unwrap() == preferences["default_locale"].as_str().unwrap() {
|
||
return String::from(preferences["default_locale"].as_str().unwrap());
|
||
}
|
||
|
||
let locale_name = std::env::var("LC_ALL").unwrap_or_else(|_| String::from("en_US.UTF-8"));
|
||
let sys_locale =
|
||
string_substr(locale_name.as_str(), 0, locale_name.find('.').unwrap_or(usize::MAX))
|
||
.unwrap();
|
||
let user_locale = format!("{}/{}/LC_MESSAGES/cachyos-hello.mo", LOCALEDIR, sys_locale);
|
||
let two_letters = string_substr(sys_locale, 0, 2).unwrap();
|
||
|
||
// If user's locale is supported
|
||
if check_regular_file(user_locale.as_str()) {
|
||
if sys_locale.contains('_') {
|
||
return sys_locale.replace('_', "-");
|
||
}
|
||
return String::from(sys_locale);
|
||
}
|
||
// If two first letters of user's locale is supported (ex: en_US -> en)
|
||
else if check_regular_file(
|
||
format!("{}/{}/LC_MESSAGES/cachyos-hello.mo", LOCALEDIR, two_letters).as_str(),
|
||
) {
|
||
return String::from(two_letters);
|
||
}
|
||
|
||
String::from(preferences["default_locale"].as_str().unwrap())
|
||
}
|
||
|
||
/// Sets locale of ui and pages.
|
||
fn set_locale(use_locale: &str) {
|
||
if PROFILE == "Devel" {
|
||
println!(
|
||
"┌{0:─^40}┐\n│{1: ^40}│\n└{0:─^40}┘",
|
||
"",
|
||
format!("Locale changed to {}", use_locale)
|
||
);
|
||
}
|
||
|
||
gettextrs::textdomain(GETTEXT_PACKAGE).expect("Unable to switch to the text domain.");
|
||
glib::setenv("LANGUAGE", use_locale, true).expect("Unable to change env variable.");
|
||
|
||
unsafe {
|
||
g_save_json.lock().unwrap()["locale"] = json!(use_locale);
|
||
}
|
||
|
||
// Real-time locale changing
|
||
let elts: HashMap<String, serde_json::Value> = serde_json::from_str(&serde_json::to_string(&json!({
|
||
"comments": ["aboutdialog"],
|
||
"label": ["autostartlabel", "development", "discover", "donate", "firstcategory", "forum", "install", "installlabel", "involved", "mailling", "readme", "release", "secondcategory", "thirdcategory", "welcomelabel", "welcometitle", "wiki"],
|
||
"tooltip_text": ["about", "development", "discover", "donate", "forum", "mailling", "wiki"],
|
||
})).unwrap()).unwrap();
|
||
|
||
let mut default_texts = json!(null);
|
||
for method in elts.iter() {
|
||
if default_texts.get(method.0) == None {
|
||
default_texts[method.0] = json![null];
|
||
}
|
||
|
||
for elt in elts[method.0].as_array().unwrap() {
|
||
let elt_value = elt.as_str().unwrap();
|
||
unsafe {
|
||
let item: gtk::Widget =
|
||
g_hello_window.clone().unwrap().builder.object(elt_value).unwrap();
|
||
if default_texts[method.0].get(elt_value) == None {
|
||
let item_buf = item.property::<String>(method.0.as_str());
|
||
default_texts[method.0][elt_value] = json!(item_buf);
|
||
}
|
||
if method.0 == "tooltip_text" || method.0 == "comments" {
|
||
item.set_property(
|
||
method.0,
|
||
&gettextrs::gettext(default_texts[method.0][elt_value].as_str().unwrap()),
|
||
);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
unsafe {
|
||
let preferences = &g_hello_window.clone().unwrap().preferences;
|
||
let save = &*g_save_json.lock().unwrap();
|
||
|
||
// Change content of pages
|
||
let pages = format!(
|
||
"{}/data/pages/{}",
|
||
PKGDATADIR,
|
||
preferences["default_locale"].as_str().unwrap()
|
||
);
|
||
for page in fs::read_dir(pages).unwrap() {
|
||
let stack: gtk::Stack =
|
||
g_hello_window.clone().unwrap().builder.object("stack").unwrap();
|
||
let child = stack.child_by_name(&format!(
|
||
"{}page",
|
||
page.as_ref().unwrap().path().file_name().unwrap().to_str().unwrap()
|
||
));
|
||
if child == None {
|
||
eprintln!("child not found");
|
||
continue;
|
||
}
|
||
let first_child = &child.unwrap().downcast::<gtk::Container>().unwrap().children();
|
||
let second_child =
|
||
&first_child[0].clone().downcast::<gtk::Container>().unwrap().children();
|
||
let third_child =
|
||
&second_child[0].clone().downcast::<gtk::Container>().unwrap().children();
|
||
|
||
let label = &third_child[0].clone().downcast::<gtk::Label>().unwrap();
|
||
label.set_markup(
|
||
get_page(
|
||
page.unwrap().path().file_name().unwrap().to_str().unwrap(),
|
||
preferences,
|
||
save,
|
||
)
|
||
.as_str(),
|
||
);
|
||
}
|
||
}
|
||
}
|
||
|
||
fn set_autostart(autostart: bool) {
|
||
let autostart_path: String;
|
||
let desktop_path: String;
|
||
unsafe {
|
||
autostart_path = fix_path(
|
||
g_hello_window.clone().unwrap().preferences["autostart_path"].as_str().unwrap(),
|
||
);
|
||
desktop_path = g_hello_window.clone().unwrap().preferences["desktop_path"]
|
||
.as_str()
|
||
.unwrap()
|
||
.to_string();
|
||
}
|
||
let config_dir = Path::new(&autostart_path).parent().unwrap();
|
||
if !config_dir.exists() {
|
||
fs::create_dir_all(config_dir).unwrap();
|
||
}
|
||
if autostart && !check_regular_file(autostart_path.as_str()) {
|
||
std::os::unix::fs::symlink(desktop_path, autostart_path).unwrap();
|
||
} else if !autostart && check_regular_file(autostart_path.as_str()) {
|
||
std::fs::remove_file(autostart_path).unwrap();
|
||
}
|
||
}
|
||
|
||
#[inline]
|
||
fn get_page(name: &str, preferences: &serde_json::Value, save: &serde_json::Value) -> String {
|
||
let mut filename =
|
||
format!("{}/data/pages/{}/{}", PKGDATADIR, save["locale"].as_str().unwrap(), name);
|
||
if !check_regular_file(filename.as_str()) {
|
||
filename = format!(
|
||
"{}/data/pages/{}/{}",
|
||
PKGDATADIR,
|
||
preferences["default_locale"].as_str().unwrap(),
|
||
name
|
||
);
|
||
}
|
||
|
||
fs::read_to_string(filename).unwrap()
|
||
}
|
||
|
||
fn load_enabled_units() {
|
||
unsafe {
|
||
g_local_units.lock().unwrap().loaded_units.clear();
|
||
g_local_units.lock().unwrap().enabled_units.clear();
|
||
|
||
let mut exec_out = Exec::shell("systemctl list-unit-files -q --no-pager | tr -s \" \"")
|
||
.stdout(Redirection::Pipe)
|
||
.capture()
|
||
.unwrap()
|
||
.stdout_str();
|
||
exec_out.pop();
|
||
|
||
let service_list = exec_out.split('\n');
|
||
|
||
for service in service_list {
|
||
let out: Vec<&str> = service.split(' ').collect();
|
||
g_local_units.lock().unwrap().loaded_units.push(out[0].to_string());
|
||
if out[1] == "enabled" {
|
||
g_local_units.lock().unwrap().enabled_units.push(out[0].to_string());
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
fn load_global_enabled_units() {
|
||
unsafe {
|
||
g_global_units.lock().unwrap().loaded_units.clear();
|
||
g_global_units.lock().unwrap().enabled_units.clear();
|
||
|
||
let mut exec_out =
|
||
Exec::shell("systemctl --global list-unit-files -q --no-pager | tr -s \" \"")
|
||
.stdout(Redirection::Pipe)
|
||
.capture()
|
||
.unwrap()
|
||
.stdout_str();
|
||
exec_out.pop();
|
||
|
||
let service_list = exec_out.split('\n');
|
||
for service in service_list {
|
||
let out: Vec<&str> = service.split(' ').collect();
|
||
g_global_units.lock().unwrap().loaded_units.push(out[0].to_string());
|
||
if out[1] == "enabled" {
|
||
g_global_units.lock().unwrap().enabled_units.push(out[0].to_string());
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/// Handlers
|
||
fn on_languages_changed(param: &[glib::Value]) -> Option<glib::Value> {
|
||
let widget = param[0].get::<gtk::ComboBox>().unwrap();
|
||
let active_id = widget.active_id().unwrap();
|
||
|
||
set_locale(active_id.as_str());
|
||
|
||
None
|
||
}
|
||
|
||
fn on_action_clicked(param: &[glib::Value]) -> Option<glib::Value> {
|
||
let widget = param[0].get::<gtk::Widget>().unwrap();
|
||
return match widget.widget_name().as_str() {
|
||
"install" => {
|
||
quick_message("Calamares install type");
|
||
None
|
||
},
|
||
"autostart" => {
|
||
let action = widget.downcast::<gtk::Switch>().unwrap();
|
||
set_autostart(action.is_active());
|
||
None
|
||
},
|
||
_ => {
|
||
show_about_dialog();
|
||
None
|
||
},
|
||
};
|
||
}
|
||
|
||
fn on_servbtn_clicked(button: >k::CheckButton) {
|
||
// Get action data/type.
|
||
let action_type: &str;
|
||
let action_data: &str;
|
||
unsafe {
|
||
action_type = *button.data("actionType").unwrap().as_ptr();
|
||
action_data = *button.data("actionData").unwrap().as_ptr();
|
||
}
|
||
|
||
let (user_only, pkexec_only) =
|
||
if action_type == "user_service" { ("--user", "--user $(logname)") } else { ("", "") };
|
||
|
||
let cmd: String;
|
||
unsafe {
|
||
let local_units = &g_local_units.lock().unwrap().enabled_units;
|
||
cmd = if !local_units.contains(&String::from(action_data)) {
|
||
format!(
|
||
"/sbin/pkexec {} bash -c \"systemctl {} enable --now --force {}\"",
|
||
pkexec_only, user_only, action_data
|
||
)
|
||
} else {
|
||
format!(
|
||
"/sbin/pkexec {} bash -c \"systemctl {} disable --now {}\"",
|
||
pkexec_only, user_only, action_data
|
||
)
|
||
};
|
||
}
|
||
|
||
// Spawn child process in separate thread.
|
||
std::thread::spawn(move || {
|
||
Exec::shell(cmd).join().unwrap();
|
||
|
||
if action_type == "user_service" {
|
||
load_global_enabled_units();
|
||
} else {
|
||
load_enabled_units();
|
||
}
|
||
});
|
||
}
|
||
|
||
fn on_appbtn_clicked(button: >k::Button) {
|
||
// Get button label.
|
||
let name = button.label().unwrap();
|
||
let (binname, is_sudo) = if name == "CachyOS PackageInstaller" {
|
||
("cachyos-pi-bin", true)
|
||
} else if name == "CachyOS Kernel Manager" {
|
||
("cachyos-kernel-manager", false)
|
||
} else {
|
||
("", false)
|
||
};
|
||
|
||
// Check if executable exists.
|
||
let exit_status = Exec::cmd("which").arg(binname).join().unwrap();
|
||
if !exit_status.success() {
|
||
return;
|
||
}
|
||
|
||
let mut envs = String::new();
|
||
for env in glib::listenv() {
|
||
if env == "PATH" {
|
||
envs += "PATH=/sbin:/bin:/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/sbin ";
|
||
continue;
|
||
}
|
||
let _ = write!(
|
||
envs,
|
||
"{}=\"{}\" ",
|
||
env.to_str().unwrap(),
|
||
glib::getenv(&env).unwrap().to_str().unwrap()
|
||
);
|
||
}
|
||
|
||
// Get executable path.
|
||
let mut exe_path =
|
||
Exec::cmd("which").arg(binname).stdout(Redirection::Pipe).capture().unwrap().stdout_str();
|
||
exe_path.pop();
|
||
let bash_cmd = format!("{} {}", &envs, &exe_path);
|
||
|
||
// Create context channel.
|
||
let (tx, rx) = glib::MainContext::channel(glib::PRIORITY_DEFAULT);
|
||
|
||
// Spawn child process in separate thread.
|
||
std::thread::spawn(move || {
|
||
let exit_status = if is_sudo {
|
||
Exec::cmd("/sbin/pkexec").arg("bash").arg("-c").arg(bash_cmd).join().unwrap()
|
||
} else {
|
||
Exec::shell(bash_cmd).join().unwrap()
|
||
};
|
||
tx.send(format!("Exit status successfully? = {:?}", exit_status.success()))
|
||
.expect("Couldn't send data to channel");
|
||
});
|
||
|
||
rx.attach(None, move |text| {
|
||
println!("{}", text);
|
||
glib::Continue(true)
|
||
});
|
||
}
|
||
|
||
fn on_btn_clicked(param: &[glib::Value]) -> Option<glib::Value> {
|
||
let widget = param[0].get::<gtk::Button>().unwrap();
|
||
let name = widget.widget_name();
|
||
|
||
unsafe {
|
||
let stack: gtk::Stack = g_hello_window.clone().unwrap().builder.object("stack").unwrap();
|
||
stack.set_visible_child_name(&format!("{}page", name));
|
||
};
|
||
|
||
None
|
||
}
|
||
|
||
fn on_link_clicked(param: &[glib::Value]) -> Option<glib::Value> {
|
||
let widget = param[0].get::<gtk::Widget>().unwrap();
|
||
let name = widget.widget_name();
|
||
|
||
unsafe {
|
||
let preferences = &g_hello_window.clone().unwrap().preferences["urls"];
|
||
|
||
let uri = preferences[name.as_str()].as_str().unwrap();
|
||
let _ = gtk::show_uri_on_window(gtk::Window::NONE, uri, 0);
|
||
}
|
||
|
||
None
|
||
}
|
||
|
||
fn on_delete_window(_param: &[glib::Value]) -> Option<glib::Value> {
|
||
unsafe {
|
||
let preferences = &g_hello_window.clone().unwrap().preferences["save_path"];
|
||
let save = &*g_save_json.lock().unwrap();
|
||
write_json(preferences.as_str().unwrap(), save);
|
||
}
|
||
|
||
Some(false.to_value())
|
||
}
|