Compare commits

...

16 Commits

Author SHA256 Message Date
kira c56da21eab Adding initial stuff for install stage. 2026-06-06 00:28:29 +02:00
kira ed6ffcdee1 Changes to partition stage in respect to intall stage. 2026-06-06 00:27:55 +02:00
kira 4d8c9540d0 Added function to KiraSize to format size with B suffix. 2026-06-06 00:24:36 +02:00
kira 56e87a62cf Adding records for install stage. 2026-06-06 00:23:09 +02:00
kira 9769ae409e Functions for creating partitions and filesystems. 2026-06-06 00:21:34 +02:00
kira 3abbbe9abd Addin saving of previos stages for easy going back. 2026-05-30 22:12:08 +02:00
kira b2f510e679 Security stage (user login) mostly completed. 2026-05-30 17:18:54 +02:00
kira a503b2a36f Fixes and initial work on security stage. 2026-05-30 00:33:34 +02:00
kira f0a8e704c0 Using secure boot status to determin its awailability for installation. 2026-05-28 00:43:31 +02:00
kira 187129d314 Added secure boot status to sysinfo. 2026-05-28 00:42:31 +02:00
kira b0ea2e0ee3 Properly hadnle swap partition size calculation with regart to zram option. 2026-05-26 23:46:36 +02:00
kira 4fb49797cb Made option (reuse network settings) on by default. 2026-05-26 23:45:38 +02:00
kira bb3f5ed1f0 Work on Partition Stage, added custom swap size selection. 2026-05-26 22:54:19 +02:00
kira 078589332c Adding build.rs for config file copy and changing render backend to tiny-skia. 2026-05-26 18:26:56 +02:00
kira 6b4550dc8e Changing println to proper log macros and small fixes. 2026-05-26 17:56:02 +02:00
kira e0c6112422 Adding simple logger implementation to use with log crate. 2026-05-26 17:47:20 +02:00
19 changed files with 1199 additions and 930 deletions
Generated
+82 -753
View File
File diff suppressed because it is too large Load Diff
+18 -4
View File
@@ -3,18 +3,32 @@ name = "kira-installer"
version = "0.1.0" version = "0.1.0"
edition = "2024" edition = "2024"
# Reference the build script
# build = "build.rs"
[dependencies] [dependencies]
blocking = "1.6" blocking = "1.6"
iced = { version = "0.14", default-features = false, features = ["crisp", "linux-theme-detection", "web-colors", "wgpu", "x11", "wayland", "smol", "image"] } iced = { version = "0.14", default-features = false, features = ["crisp", "linux-theme-detection", "tiny-skia", "x11", "smol", "image"] }
iced_moving_picture = "0" iced_moving_picture = "0"
log = "0.4"
rust-i18n = "3" rust-i18n = "3"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
time-format = "1.2"
toml = "1" toml = "1"
[dev-dependencies] [dev-dependencies]
rand = "0.10" rand = "0.10"
[profile.release] [profile.dev]
#lto = true
codegen-units = 16
opt-level = 2 opt-level = 2
codegen-units = 16
[profile.release]
lto = true
debug = false
incremental = false
codegen-units = 1
opt-level = 3
strip = true strip = true
+23
View File
@@ -0,0 +1,23 @@
use std::env;
use std::fs;
use std::path::PathBuf;
fn main() {
let profile = env::var("PROFILE").unwrap();
let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
let target_dir = PathBuf::from(&manifest_dir).join("target").join(&profile);
// Ensure target directory exists
if !target_dir.exists() {
fs::create_dir_all(&target_dir).ok();
}
// Copy the file (e.g., config.json) to the target directory
let source = PathBuf::from(&manifest_dir).join("src").join("kira_config.toml");
let dest = target_dir.join("kira_config.toml");
fs::copy(&source, &dest).expect("Failed to copy kira_config.toml");
// Optional: Tell Cargo to re-run this script if the source file changes
println!("cargo:rerun-if-changed=src/kira_config.toml");
}
+12 -5
View File
@@ -1,4 +1,4 @@
use iced::widget::{Row, container, text}; use iced::widget::{self, Row, container, text};
use iced::{Alignment, Color, Element, Length}; use iced::{Alignment, Color, Element, Length};
// Assuming your Item struct or type implements Display or has a text representation // Assuming your Item struct or type implements Display or has a text representation
@@ -6,10 +6,9 @@ pub fn color_bar<Message: 'static>(
maybe_items: Option<Vec<(String, u16, Color)>>, maybe_items: Option<Vec<(String, u16, Color)>>,
bar_height: u32, bar_height: u32,
) -> Element<'static, Message> { ) -> Element<'static, Message> {
let mut c_b = Row::new().height(bar_height);
if let Some(items) = maybe_items { if let Some(items) = maybe_items {
let mut c_b = Row::new().height(Length::Fill);
for (bar_text, bar_fill, bar_color) in items { for (bar_text, bar_fill, bar_color) in items {
c_b = c_b.push( c_b = c_b.push(
container(text(bar_text).color(Color::BLACK).size(iced::Pixels(14.0))) container(text(bar_text).color(Color::BLACK).size(iced::Pixels(14.0)))
@@ -21,9 +20,17 @@ pub fn color_bar<Message: 'static>(
.padding(2), .padding(2),
); );
} }
} container(c_b.spacing(2))
container(c_b.spacing(2))
.style(container::rounded_box) .style(container::rounded_box)
.padding(3) .padding(3)
.height(bar_height)
.into() .into()
}
else {
widget::container(widget::space())
.height(bar_height)
.width(Length::Fill)
.into()
}
} }
+235 -24
View File
@@ -18,9 +18,9 @@
Functions for working with disk layout Functions for working with disk layout
*/ */
use crate::kira_size::KiraSize;
use std::collections::HashMap; use log;
use crate::kira_size::{KiraSize}; use std::{collections::HashMap, io::Read, process::ExitStatus};
/// ///
/// size - device size in bytes /// size - device size in bytes
/// sector size - default 4096 /// sector size - default 4096
@@ -38,21 +38,31 @@ impl std::fmt::Display for BlkDev {
} }
impl BlkDev { impl BlkDev {
/// Create new BlkDev struct with specifyed name and size
pub fn new(name: String, size: KiraSize) -> Self { pub fn new(name: String, size: KiraSize) -> Self {
Self { Self {
name: name, name: name,
size: size, size: size,
sector_size: KiraSize::new(4096), sector_size: KiraSize::new_b(4096),
} }
} }
/// Create new BlkDev struct with name and size parsed from hashmap
/// that contains String key val pairs with "NAME" and "SIZE" keys
pub fn from_hash_map(data: &HashMap<String, String>) -> Option<Self> { pub fn from_hash_map(data: &HashMap<String, String>) -> Option<Self> {
Some(Self { Some(Self {
name: data.get("NAME")?.clone(), name: data.get("NAME")?.clone(),
size: KiraSize::from_str_bytes(data.get("SIZE")?)?, size: KiraSize::from_str_bytes(data.get("SIZE")?)?,
sector_size: KiraSize::new(4096), sector_size: KiraSize::new_b(4096),
}) })
} }
/// returns dev name in a "/dev/name" form
pub fn full_name(&self) -> String {
format!("/dev/{}", self.name)
}
/// this is blocking function /// this is blocking function
/// Lists block devices in system thru "lsbk" command
pub fn list_sys_blk_dev() -> Result<Vec<Self>, String> { pub fn list_sys_blk_dev() -> Result<Vec<Self>, String> {
use std::process::Command; use std::process::Command;
@@ -139,6 +149,22 @@ impl FSType {
_ => Self::UNKNOWN, _ => Self::UNKNOWN,
} }
} }
/// return fs type that can be used with parted utility
pub fn to_parted_str(&self) -> &str {
match self {
Self::VFAT => "fat32",
Self::XFS => "xfs",
Self::LUKS => "luks",
Self::SWAP => "linux-swap",
Self::EXT4 => "ext4",
Self::BTRFS => "btrfs",
// workaroud as parted do not support bcachefs partition type
Self::BCACHEFS => "bcachefs",
// lets do something generic here instead of panic
Self::UNKNOWN => "linux-swap",
}
}
} }
// fn part_type_to_color(t: &str) -> Color { // fn part_type_to_color(t: &str) -> Color {
@@ -177,13 +203,7 @@ pub struct PartInfo {
impl std::fmt::Display for PartInfo { impl std::fmt::Display for PartInfo {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!( write!(f, "{} {} {}", &self.name, &self.fs_type, self.size)
f,
"{} {} {}",
&self.name,
&self.fs_type,
self.size
)
} }
} }
@@ -192,7 +212,7 @@ impl PartInfo {
Some(Self { Some(Self {
name: data.get("NAME")?.clone(), name: data.get("NAME")?.clone(),
size: KiraSize::from_str_bytes(data.get("SIZE")?)?, size: KiraSize::from_str_bytes(data.get("SIZE")?)?,
start: KiraSize::new(0), start: KiraSize::new_b(0),
fs_type: FSType::from_str(data.get("FSTYPE")?), fs_type: FSType::from_str(data.get("FSTYPE")?),
gpt_label: data gpt_label: data
.get("PARTLABEL") .get("PARTLABEL")
@@ -268,7 +288,7 @@ fn get_dev_part_blocking(dev_name: &str) -> Result<Vec<PartInfo>, String> {
/// return none if aligment is not possibe (size of partition is less than align) /// return none if aligment is not possibe (size of partition is less than align)
pub fn align_part(size: u64, start: u64, align: u64) -> Option<(u64, u64, u64)> { pub fn align_part(size: u64, start: u64, align: u64) -> Option<(u64, u64, u64)> {
if size < align { if size < align {
println!("if size < align !!!!!!!"); log::debug!("if size < align !!!!!!!");
return None; return None;
} }
@@ -283,15 +303,16 @@ pub fn align_part(size: u64, start: u64, align: u64) -> Option<(u64, u64, u64)>
// decreasing end if not align // decreasing end if not align
let part_end = align_start + size; let part_end = align_start + size;
let end_mod = part_end % align; let end_mod = part_end % align;
println!("part_end: {} end_mod: {}", part_end, end_mod); log::debug!("part_end: {} end_mod: {}", part_end, end_mod);
let align_end = part_end - end_mod - 1; let align_end = part_end - end_mod - 1;
let align_size = align_end - align_start + 1; let align_size = align_end - align_start + 1;
if align_size < align { if align_size < align {
println!( log::warn!(
"if align_size < align !!!!!!! align_size: {} align: {}", "!!! align_size < align !!! align_size: {} align: {}",
align_size, align align_size,
align
); );
return None; return None;
} }
@@ -316,7 +337,9 @@ impl PartLayout {
pub fn read_from_disk(dev: &BlkDev) -> Result<Self, String> { pub fn read_from_disk(dev: &BlkDev) -> Result<Self, String> {
let part_list = get_dev_part_blocking(&dev.name)?; let part_list = get_dev_part_blocking(&dev.name)?;
let parts_size = part_list.iter().fold(KiraSize::new(0), |acc, part| acc + part.size); let parts_size = part_list
.iter()
.fold(KiraSize::new_b(0), |acc, part| acc + part.size);
Ok(Self { Ok(Self {
dev: dev.clone(), dev: dev.clone(),
empty_space: dev.size - parts_size, empty_space: dev.size - parts_size,
@@ -351,8 +374,8 @@ impl PartLayout {
let part_name = format!("{}p{}", self.dev.name, self.part_list.len() + 1); let part_name = format!("{}p{}", self.dev.name, self.part_list.len() + 1);
self.part_list.push(PartInfo { self.part_list.push(PartInfo {
name: part_name, name: part_name,
size: KiraSize::new(size), size: KiraSize::new_b(size),
start: KiraSize::new(start), start: KiraSize::new_b(start),
fs_type: fs_type, fs_type: fs_type,
gpt_label: gpt_label, gpt_label: gpt_label,
fs_label: fs_label, fs_label: fs_label,
@@ -360,7 +383,7 @@ impl PartLayout {
role: role, role: role,
}); });
self.empty_space = self.empty_space - KiraSize::new(size); self.empty_space = self.empty_space - KiraSize::new_b(size);
self self
} }
@@ -392,7 +415,7 @@ impl PartLayout {
) )
.add_part_reserve( .add_part_reserve(
swap_size, swap_size,
FSType::XFS, FSType::BTRFS,
Some("root".into()), Some("root".into()),
None, None,
Some("/".into()), Some("/".into()),
@@ -415,6 +438,195 @@ impl PartLayout {
} }
} }
/// Executes parted command with given arguments
fn exec_parted(args: &Vec<&str>) -> Result<String, String> {
use std::process::Command;
match Command::new("parted").args(args).output() {
Err(ex) => Err(ex.to_string()),
Ok(out) => {
if out.status.success() {
String::from_utf8(out.stdout).map_err(|ex| ex.to_string())
} else {
Err(out.status.to_string())
}
}
}
}
fn parted_get_last_part_num(dev: &BlkDev) -> Result<u32, String> {
// parted -m /dev/loop0 print
match exec_parted(&vec!["-m", dev.full_name().as_str(), "print"]) {
Ok(res) => {
match res.lines().last().and_then(|last_line| {
last_line
.split_once(':')
.and_then(|(first_part, a)| u32::from_str_radix(first_part, 10).ok())
}) {
Some(n) => Ok(n),
None => Err("Error parsing parted output".to_string()),
}
}
Err(ex) => Err(ex),
}
}
/// init gpt partition table
/// parted --script $install_device mklabel gpt
/// Its blocking function
pub fn parted_init_gpt_part_table(dev: &BlkDev) -> Result<(), String> {
exec_parted(&vec![
"--script",
dev.full_name().as_str(),
"mklabel",
"gpt",
])
.map(|_| ())
}
const LINUX_ROOT_X86_64_TYPE: &str = "4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709";
/// creating partition from PartInfo specification
/// It assumes that we add partitions sequentially to empty space (for part attributes settings)
/// Otherwise can produce bonkers layout
pub fn parted_mkpart(dev: &BlkDev, part: &PartInfo) -> Result<(), String> {
let part_end = part.start + part.size;
// gpt label should be in double qutes
let gpt_label = if let Some(s) = &part.gpt_label {
format!("\"{}\"", s)
} else {
"\"\"".to_string()
};
// creating partition
exec_parted(&vec![
"--script",
dev.full_name().as_str(),
"mkpart",
gpt_label.as_str(),
part.fs_type.to_parted_str(),
part.start.to_parted_bytes_str().as_str(),
part_end.to_parted_bytes_str().as_str(),
])?;
// set partition options depending on its role
if let Some(role) = &part.role {
// getting part number of last created partition (hopefully)
let part_id = parted_get_last_part_num(dev)?;
match role {
PartRole::EFI => {
exec_parted(&vec![
"--script",
dev.full_name().as_str(),
"set",
&part_id.to_string(),
"esp",
"on",
])?;
}
// parted --script $install_device type 3 4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709
PartRole::ROOT => {
exec_parted(&vec![
"--script",
dev.full_name().as_str(),
"type",
&part_id.to_string(),
LINUX_ROOT_X86_64_TYPE,
])?;
}
_ => (),
}
};
Ok(())
}
/// creating single drive bcachefs volume
pub fn bcachefs_format(
dev: &BlkDev,
compression: Option<String>,
password: Option<String>,
) -> Result<(), String> {
// sudo bcachefs format --compression=lz4 --background_compression=lz4 --encrypted /dev/loop0
// --data_checksum=none - do not make sense on single dev volumes
// sudo bcachefs format --force --compression=lz4 --background_compression=lz4 --encrypted --passphrase_file=./test.pass /dev/loop0
use std::fs;
use std::process::Command;
let mut arg = vec!["format", "--force", "--data_checksum=none"];
// if compression algorythm specified
if let Some(compresss_alg) = &compression {
arg.push(compresss_alg.as_str());
}
// if we want encrypted volume
if let Some(passw) = password {
// creating tmp file with password
fs::write("/tmp/bzzpsspass.txt", &passw).map_err(|ex| ex.to_string())?;
arg.push("--encrypted");
arg.push("--passphrase_file=/tmp/bzzpsspass.txt");
}
// add dev name at the end
let dev_name = dev.full_name();
arg.push(dev_name.as_str());
let out = Command::new("bcachefs")
.args(arg)
.output()
.map_err(|ex| ex.to_string())?;
// ignore any errors while deleting pass tmp file
let _ = fs::remove_file("/tmp/bzzpsspass.txt");
if out.status.success() {
Ok(())
} else {
Err(String::from_utf8(out.stderr).map_err(|ex| ex.to_string())?)
}
}
/// unlok encrypted bcache volume
pub fn bcachefs_unlock(dev: &BlkDev, pass: String) -> Result<(), String> {
use std::fs;
use std::process::Command;
// bcachefs unlock --file=/tmp/bzzpsspass.txt /dev/loop0
let dev_name = dev.full_name();
let arg = vec!["unlock", "--file=/tmp/bzzpsspass.txt ", dev_name.as_str()];
// creating tmp file with password
fs::write("/tmp/bzzpsspass.txt", &pass).map_err(|ex| ex.to_string())?;
let out = Command::new("bcachefs")
.args(arg)
.output()
.map_err(|ex| ex.to_string())?;
// ignore any errors while deleting pass tmp file
let _ = fs::remove_file("/tmp/bzzpsspass.txt");
if out.status.success() {
Ok(())
} else {
Err(String::from_utf8(out.stderr).map_err(|ex| ex.to_string())?)
}
}
/// create subvolume
pub fn bcachefs_create_subvolume(vol_path: &str) -> Result<(), String> {
use std::process::Command;
// sudo bcachefs subvolume create /mnt/test/nyan
let out = Command::new("bcachefs")
.args(["subvolume", "create", vol_path])
.output()
.map_err(|ex| ex.to_string())?;
if out.status.success() {
Ok(())
} else {
Err(String::from_utf8(out.stderr).map_err(|ex| ex.to_string())?)
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use rand::RngExt; use rand::RngExt;
@@ -466,4 +678,3 @@ mod tests {
} }
} }
} }
+63
View File
@@ -0,0 +1,63 @@
use log::{Level, Metadata, Record, SetLoggerError, LevelFilter};
use std::fs::{File, OpenOptions};
use std::io::Write;
use std::sync::Mutex;
use std::sync::LazyLock;
use time_format;
struct KiraLogger {
file: Option<Mutex<File>>,
}
impl KiraLogger {
pub fn open(file_name: Option<&str>) -> std::io::Result<Self> {
match file_name {
Some(f_n) => Ok(Self {
file: Some(Mutex::new(
OpenOptions::new().append(true).create(true).open(f_n)?,
)),
}),
None => Ok(Self {
file: None,
}),
}
}
}
impl log::Log for KiraLogger {
fn enabled(&self, metadata: &Metadata) -> bool {
(metadata.target().starts_with("kira_installer") && metadata.level() <= Level::Debug) || (metadata.level() <= Level::Error)
}
fn log(&self, record: &Record) {
if self.enabled(record.metadata()) {
let ts = time_format::now().unwrap();
let local_time = time_format::strftime_local("%Y-%m-%dT%H:%M:%S", ts).unwrap();
if let Some(m_file) = &self.file {
let mut write_lock = m_file.lock().unwrap();
let _ = writeln!(&mut *write_lock, "{} {}: {}", local_time, record.level(), record.args());
}
println!("{} {} {}: {}", local_time,record.metadata().target(), record.level(), record.args());
}
}
fn flush(&self) {
if let Some(m_file) = &self.file {
let mut write_lock = m_file.lock().unwrap();
let _ = (&mut *write_lock).flush();
}
}
}
static LOGGER: LazyLock<KiraLogger> = LazyLock::new(|| {
let ts = time_format::now().unwrap();
// Local time with timezone name
let local_time = time_format::strftime_local("%Y-%m-%d_%H-%M-%S", ts).unwrap();
KiraLogger::open(Some(format!("installation_{}.log", local_time).as_str())).unwrap()
});
pub fn init() -> Result<(), SetLoggerError> {
log::set_logger(&*LOGGER)
.map(|()| log::set_max_level(LevelFilter::Debug))
}
+9 -21
View File
@@ -20,25 +20,6 @@
use std::ops::{Add, Sub}; use std::ops::{Add, Sub};
pub fn format_bytes_size(size: u64) -> String {
let suffixs: [&str; 5] = ["KB", "MB", "GB", "TB", "PB"];
if size < 1000 {
format!("{}B", size)
} else {
let mut s = size as f64;
let mut idx = 0;
for i in 0..5 {
s = s / 1024.0;
idx = i;
if s < 1000.0 {
break;
}
}
format!("{:.2}{}", s, suffixs[idx])
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct KiraSize { pub struct KiraSize {
size_bytes: u64, size_bytes: u64,
@@ -65,7 +46,14 @@ impl std::fmt::Display for KiraSize {
impl KiraSize { impl KiraSize {
pub const SUFFIXS: [&str; 5] = ["KB", "MB", "GB", "TB", "PB"]; pub const SUFFIXS: [&str; 5] = ["KB", "MB", "GB", "TB", "PB"];
pub fn new(size:u64) -> Self {
/// returns size in bytes suffixed with "B" character
pub fn to_parted_bytes_str(&self)-> String {
format!("{}B", self.size_bytes)
}
pub fn new_b(size:u64) -> Self {
Self { size_bytes: size } Self { size_bytes: size }
} }
pub fn new_kb(size:u64) -> Self { pub fn new_kb(size:u64) -> Self {
@@ -104,7 +92,7 @@ impl KiraSize {
} }
pub fn from_str_bytes(s: &str) -> Option<Self> { pub fn from_str_bytes(s: &str) -> Option<Self> {
Some(Self::new(u64::from_str_radix(s, 10).ok()?)) Some(Self::new_b(u64::from_str_radix(s, 10).ok()?))
} }
pub fn div_c(self, other: u64) -> Self { pub fn div_c(self, other: u64) -> Self {
+34
View File
@@ -95,3 +95,37 @@ pub fn get_gpus_list() -> Vec<GpuInfo> {
res res
} }
use serde::Deserialize;
#[derive(Debug, Clone, Deserialize)]
pub struct SecureBootStatus {
pub installed: bool,
pub guid: String,
pub setup_mode: bool,
pub secure_boot: bool,
pub vendors: Vec<String>,
pub firmware_quirks: Vec<String>,
}
pub fn get_secure_boot_status() -> Result<SecureBootStatus, String> {
use std::process::Command;
//sbctl --json status
match Command::new("sbctl").args(["--json", "status"]).output() {
Ok(cmd_output) => {
if cmd_output.status.success() {
match serde_json::from_slice(&cmd_output.stdout) {
Ok(res) => Ok(res),
Err(ex) => Err(ex.to_string()),
}
} else {
Err(format!(
"Error getting disk devices list: {}",
String::from_utf8(cmd_output.stderr).unwrap_or("UNKNOWN".into())
))
}
}
Err(ex) => Err(ex.to_string()),
}
}
+16 -2
View File
@@ -4,7 +4,10 @@
"button.next": "Next", "button.next": "Next",
"button.back": "Back", "button.back": "Back",
"button.cancel": "Cancel", "button.cancel": "Cancel",
"button.finish": "Finish",
"button.exit": "Exit", "button.exit": "Exit",
"button.yes": "Yes",
"button.no": "No",
"wellcome.text": "Welcome to Kira Installer!", "wellcome.text": "Welcome to Kira Installer!",
"wellcome.choose_language": "Please select language to use during istalation!", "wellcome.choose_language": "Please select language to use during istalation!",
"license.license": "This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.", "license.license": "This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.",
@@ -30,13 +33,24 @@
"partition.no_swap": "No swap partition", "partition.no_swap": "No swap partition",
"partition.swap_no_hibernate": "Swap without hibernate", "partition.swap_no_hibernate": "Swap without hibernate",
"partition.swap_hibernate": "Swap with hibernation", "partition.swap_hibernate": "Swap with hibernation",
"partition.swap_custom": "Custom swap size",
"partition.select_device_placeholder": "Select Device", "partition.select_device_placeholder": "Select Device",
"partition.select_device": "Select device for system installation", "partition.select_device": "Select device for system installation",
"partition.select_swapmode_placeholder": "Select Swap Mode", "partition.select_swapmode_placeholder": "Select Swap Mode",
"partition.select_swapmode": "Select swap partition mode", "partition.select_swapmode": "Select swap partition mode",
"partition.use_zram": "Use zram technology", "partition.use_zram": "Use zram technology",
"partition.use_secure_boot": "Setup secure boot", "partition.use_secure_boot": "Setup secure boot",
"partition.encrypt_drive": "Encrypt drive",
"partition.current_dev_layout": "Current device layout", "partition.current_dev_layout": "Current device layout",
"partition.current_after_install": "After installation device will look like this" "partition.current_after_install": "After installation device will look like this",
"security.user_name": "User login name",
"security.user_full_name": "User full name/description",
"security.user_password": "User password",
"security.reuse_user_password": "Reuse user password for root user",
"security.root_password": "Root password",
"security.pass_low": "low security",
"security.pass_middle": "normal security",
"security.pass_hight": "hight security",
"install.caption": "Now we will beee install Bzz Linux to your PC",
"install.warning": "We a ready to start installation, this action will destroy all data on select drive!\nDo you want to proceed?"
} }
+67 -45
View File
@@ -21,6 +21,7 @@
*/ */
use iced::widget; use iced::widget;
use log;
use std::process::ExitCode; use std::process::ExitCode;
use iced::{Element, Task}; use iced::{Element, Task};
@@ -32,12 +33,13 @@ use crate::stages::welcome;
use crate::stages::welcome::WelcomeStage; use crate::stages::welcome::WelcomeStage;
rust_i18n::i18n!("src/locales", fallback = "en"); rust_i18n::i18n!("src/locales", fallback = "en");
mod kira_theming;
mod kira_scroll_list;
mod kira_color_bar; mod kira_color_bar;
mod kira_disk_layout; mod kira_disk_layout;
mod kira_logger;
mod kira_scroll_list;
mod kira_size; mod kira_size;
mod kira_sysinfo; mod kira_sysinfo;
mod kira_theming;
mod stage; mod stage;
mod stages; mod stages;
@@ -50,6 +52,7 @@ enum Views {
Locale(stages::locale::LocaleStage), Locale(stages::locale::LocaleStage),
Keyboard(stages::keyboard::KeyboardStage), Keyboard(stages::keyboard::KeyboardStage),
Partition(stages::partition::PartitionStage), Partition(stages::partition::PartitionStage),
Security(stages::security::SecurityStage),
} }
enum Message { enum Message {
@@ -61,12 +64,14 @@ enum Message {
Locale(stages::locale::Message), Locale(stages::locale::Message),
Keyboard(stages::keyboard::Message), Keyboard(stages::keyboard::Message),
Partition(stages::partition::Message), Partition(stages::partition::Message),
Security(stages::security::Message),
} }
struct KiraState { struct KiraState {
current_view: Views, current_view: Views,
toml_config: toml::Table, toml_config: toml::Table,
config: KiraConfig, config: KiraConfig,
stages_save: Vec<Views>,
} }
const CONFIG_PATH_STR: &str = "kira_config.toml"; const CONFIG_PATH_STR: &str = "kira_config.toml";
@@ -80,7 +85,7 @@ fn load_kira_config(config_path: &str) -> Result<toml::Table, Box<dyn std::error
// 2. Parse the string into a Table // 2. Parse the string into a Table
let table: Table = content.parse()?; let table: Table = content.parse()?;
println!("{:?}", table); log::info!("{:?}", table);
Ok(table) Ok(table)
} }
@@ -93,13 +98,13 @@ impl KiraState {
config: KiraConfig { config: KiraConfig {
config_trail: Vec::new(), config_trail: Vec::new(),
}, },
stages_save: Vec::new(),
}, },
Task::done(Message::Start), Task::done(Message::Start),
) )
} }
} }
fn view(k_state: &KiraState) -> Element<'_, Message> { fn view(k_state: &KiraState) -> Element<'_, Message> {
match &k_state.current_view { match &k_state.current_view {
Views::Start => Element::new(widget::space()), Views::Start => Element::new(widget::space()),
@@ -110,6 +115,7 @@ fn view(k_state: &KiraState) -> Element<'_, Message> {
Views::Locale(locale_stage) => locale_stage.view().map(Message::Locale), Views::Locale(locale_stage) => locale_stage.view().map(Message::Locale),
Views::Keyboard(keyboard_stage) => keyboard_stage.view().map(Message::Keyboard), Views::Keyboard(keyboard_stage) => keyboard_stage.view().map(Message::Keyboard),
Views::Partition(partition_stage) => partition_stage.view().map(Message::Partition), Views::Partition(partition_stage) => partition_stage.view().map(Message::Partition),
Views::Security(security_stage) => security_stage.view().map(Message::Security)
} }
} }
@@ -117,13 +123,13 @@ fn update(k_state: &mut KiraState, message: Message) -> Task<Message> {
match message { match message {
Message::Start => match load_kira_config(CONFIG_PATH_STR) { Message::Start => match load_kira_config(CONFIG_PATH_STR) {
Ok(conf) => { Ok(conf) => {
println!("Config loaded!"); log::info!("Config loaded!");
k_state.toml_config = conf; k_state.toml_config = conf;
k_state.current_view = Views::Welcome(WelcomeStage::new()); k_state.current_view = Views::Welcome(WelcomeStage::new());
Task::none() Task::none()
} }
Err(ex) => { Err(ex) => {
println!("Error reading config {}: {}", CONFIG_PATH_STR, ex); log::error!("Error reading config {}: {}", CONFIG_PATH_STR, ex);
iced::exit() iced::exit()
} }
}, },
@@ -132,7 +138,8 @@ fn update(k_state: &mut KiraState, message: Message) -> Task<Message> {
match wlc_view.update(wlc_msg) { match wlc_view.update(wlc_msg) {
StageAction::Next(welcome_res) => { StageAction::Next(welcome_res) => {
k_state.config.config_trail.push(welcome_res); k_state.config.config_trail.push(welcome_res);
k_state.current_view = Views::License(license::LicenseStage {}); let s_state = std::mem::replace(&mut k_state.current_view, Views::License(license::LicenseStage {}));
k_state.stages_save.push(s_state);
Task::none() Task::none()
} }
StageAction::Abort => iced::exit(), StageAction::Abort => iced::exit(),
@@ -141,20 +148,20 @@ fn update(k_state: &mut KiraState, message: Message) -> Task<Message> {
} else { } else {
Task::none() Task::none()
} }
}, }
Message::License(license_message) => { Message::License(license_message) => {
if let Views::License(license_view) = &mut k_state.current_view { if let Views::License(license_view) = &mut k_state.current_view {
let action = license_view.update(license_message); let action = license_view.update(license_message);
match action { match action {
StageAction::Next(license_res) => { StageAction::Next(license_res) => {
k_state.config.config_trail.push(license_res); k_state.config.config_trail.push(license_res);
k_state.current_view = let s_state = std::mem::replace(&mut k_state.current_view, Views::Network(network::NetworkStage::new(&k_state.toml_config)));
Views::Network(network::NetworkStage::new(&k_state.toml_config)); k_state.stages_save.push(s_state);
Task::done(Message::Network(network::Message::CheckNetwork)) Task::done(Message::Network(network::Message::CheckNetwork))
} }
StageAction::Abort => iced::exit(), StageAction::Abort => iced::exit(),
StageAction::Back => { StageAction::Back => {
k_state.current_view = Views::Welcome(WelcomeStage::new()); k_state.current_view = k_state.stages_save.pop().unwrap();
k_state.config.config_trail.pop(); k_state.config.config_trail.pop();
Task::none() Task::none()
} }
@@ -163,7 +170,7 @@ fn update(k_state: &mut KiraState, message: Message) -> Task<Message> {
} else { } else {
Task::none() Task::none()
} }
}, }
Message::Network(network_message) => { Message::Network(network_message) => {
if let Views::Network(network_view) = &mut k_state.current_view { if let Views::Network(network_view) = &mut k_state.current_view {
let update_result = network_view.update(network_message); let update_result = network_view.update(network_message);
@@ -172,12 +179,12 @@ fn update(k_state: &mut KiraState, message: Message) -> Task<Message> {
network::UpdateResult::StageAction(action) => match action { network::UpdateResult::StageAction(action) => match action {
StageAction::Next(network_res) => { StageAction::Next(network_res) => {
k_state.config.config_trail.push(network_res); k_state.config.config_trail.push(network_res);
k_state.current_view = let s_state = std::mem::replace(&mut k_state.current_view,Views::TimeZone(stages::timezone::TimeZoneStage::new()));
Views::TimeZone(stages::timezone::TimeZoneStage::new()); k_state.stages_save.push(s_state);
Task::none() Task::none()
} }
StageAction::Back => { StageAction::Back => {
k_state.current_view = Views::License(license::LicenseStage {}); k_state.current_view = k_state.stages_save.pop().unwrap();
k_state.config.config_trail.pop(); k_state.config.config_trail.pop();
Task::none() Task::none()
} }
@@ -187,41 +194,41 @@ fn update(k_state: &mut KiraState, message: Message) -> Task<Message> {
} else { } else {
Task::none() Task::none()
} }
}, }
Message::TimeZone(timezone_message) => { Message::TimeZone(timezone_message) => {
if let Views::TimeZone(timezone_view) = &mut k_state.current_view { if let Views::TimeZone(timezone_view) = &mut k_state.current_view {
let action = timezone_view.update(timezone_message); let action = timezone_view.update(timezone_message);
match action { match action {
StageAction::Next(tz_res) => { StageAction::Next(tz_res) => {
k_state.config.config_trail.push(tz_res); k_state.config.config_trail.push(tz_res);
k_state.current_view = Views::Locale(stages::locale::LocaleStage::new(&k_state.config)); let s_state = std::mem::replace(&mut k_state.current_view,Views::Locale(stages::locale::LocaleStage::new(&k_state.config)));
k_state.stages_save.push(s_state);
Task::none() Task::none()
} }
StageAction::Abort => iced::exit(), StageAction::Abort => iced::exit(),
StageAction::Back => { StageAction::Back => {
k_state.config.config_trail.pop(); k_state.config.config_trail.pop();
k_state.current_view = k_state.current_view = k_state.stages_save.pop().unwrap();
Views::Network(network::NetworkStage::new(&k_state.toml_config)); Task::none()
Task::done(Message::Network(network::Message::CheckNetwork))
} }
StageAction::None => Task::none(), StageAction::None => Task::none(),
} }
} else { } else {
Task::none() Task::none()
} }
}, }
Message::Locale(locale_msg) => { Message::Locale(locale_msg) => {
if let Views::Locale(locale_view) = &mut k_state.current_view { if let Views::Locale(locale_view) = &mut k_state.current_view {
match locale_view.update(locale_msg) { match locale_view.update(locale_msg) {
StageAction::Next(locale_res) => { StageAction::Next(locale_res) => {
k_state.config.config_trail.push(locale_res); k_state.config.config_trail.push(locale_res);
k_state.current_view = Views::Keyboard(stages::keyboard::KeyboardStage::new()); let s_state = std::mem::replace(&mut k_state.current_view,Views::Keyboard(stages::keyboard::KeyboardStage::new()));
k_state.stages_save.push(s_state);
Task::none() Task::none()
} }
StageAction::Back => { StageAction::Back => {
k_state.config.config_trail.pop(); k_state.config.config_trail.pop();
k_state.current_view = k_state.current_view = k_state.stages_save.pop().unwrap();
Views::TimeZone(stages::timezone::TimeZoneStage::new());
Task::none() Task::none()
} }
_ => Task::none(), _ => Task::none(),
@@ -229,7 +236,7 @@ fn update(k_state: &mut KiraState, message: Message) -> Task<Message> {
} else { } else {
Task::none() Task::none()
} }
}, }
Message::Keyboard(keyboard_msg) => { Message::Keyboard(keyboard_msg) => {
if let Views::Keyboard(keyboard_view) = &mut k_state.current_view { if let Views::Keyboard(keyboard_view) = &mut k_state.current_view {
match keyboard_view.update(keyboard_msg) { match keyboard_view.update(keyboard_msg) {
@@ -237,18 +244,18 @@ fn update(k_state: &mut KiraState, message: Message) -> Task<Message> {
match stages::partition::PartitionStage::new(&k_state.toml_config) { match stages::partition::PartitionStage::new(&k_state.toml_config) {
Ok(part_stage) => { Ok(part_stage) => {
k_state.config.config_trail.push(keyboard_res); k_state.config.config_trail.push(keyboard_res);
k_state.current_view = Views::Partition(part_stage); let s_state = std::mem::replace(&mut k_state.current_view,Views::Partition(part_stage));
}, k_state.stages_save.push(s_state);
Err(ex) => { }
println!("Error, cannot init partition stage: {}", ex); Err(ex) => {
log::error!("Error, cannot init partition stage: {}", ex);
} }
} }
Task::none() Task::none()
} }
StageAction::Back => { StageAction::Back => {
k_state.config.config_trail.pop(); k_state.config.config_trail.pop();
k_state.current_view = Views::Locale(stages::locale::LocaleStage::new(&k_state.config)); k_state.current_view = k_state.stages_save.pop().unwrap();
Task::none() Task::none()
} }
_ => Task::none(), _ => Task::none(),
@@ -262,11 +269,13 @@ fn update(k_state: &mut KiraState, message: Message) -> Task<Message> {
match partition_view.update(partition_msg) { match partition_view.update(partition_msg) {
StageAction::Next(partition_res) => { StageAction::Next(partition_res) => {
k_state.config.config_trail.push(partition_res); k_state.config.config_trail.push(partition_res);
iced::exit() let s_state = std::mem::replace(&mut k_state.current_view,Views::Security(stages::security::SecurityStage::new()));
k_state.stages_save.push(s_state);
Task::none()
} }
StageAction::Back => { StageAction::Back => {
k_state.config.config_trail.pop(); k_state.config.config_trail.pop();
k_state.current_view = Views::Keyboard(stages::keyboard::KeyboardStage::new()); k_state.current_view = k_state.stages_save.pop().unwrap();
Task::none() Task::none()
} }
_ => Task::none(), _ => Task::none(),
@@ -274,12 +283,33 @@ fn update(k_state: &mut KiraState, message: Message) -> Task<Message> {
} else { } else {
Task::none() Task::none()
} }
} },
Message::Security(security_msg) => {
if let Views::Security(security_view) = &mut k_state.current_view {
match security_view.update(security_msg) {
StageAction::Next(security_res) => {
k_state.config.config_trail.push(security_res);
//let s_state = std::mem::replace(&mut k_state.current_view,
Task::none()
}
StageAction::Back => {
k_state.config.config_trail.pop();
k_state.current_view = k_state.stages_save.pop().unwrap();
Task::none()
}
_ => Task::none(),
}
} else {
Task::none()
}
},
} }
} }
pub fn main() -> ExitCode { pub fn main() -> ExitCode {
// Init logger
kira_logger::init().unwrap();
let iced_result = iced::application(KiraState::boot, update, view) let iced_result = iced::application(KiraState::boot, update, view)
.window(iced::window::Settings { .window(iced::window::Settings {
@@ -297,16 +327,8 @@ pub fn main() -> ExitCode {
match iced_result { match iced_result {
Ok(()) => ExitCode::SUCCESS, Ok(()) => ExitCode::SUCCESS,
Err(ex) => { Err(ex) => {
println!("ICED Error: {}", ex); log::error!("ICED Error: {}", ex);
ExitCode::from(42) ExitCode::from(42)
}, // Custom error code } // Custom error code
} }
} }
// fn main() {
// println!("Hello, world!");
// println!("{}", t!("wellcome.text"));
// // Initialize the state
// let _res = main_interface();
// }
+4
View File
@@ -72,6 +72,10 @@ impl StageResult {
self.add_val(val_name, ConfigValue::Bool(v)) self.add_val(val_name, ConfigValue::Bool(v))
} }
pub fn add_val_u64(self, val_name: &str, v: u64) -> Self {
self.add_val(val_name, ConfigValue::U64(v))
}
pub fn get_val(&self, val_name: &str) -> Option<ConfigValue> { pub fn get_val(&self, val_name: &str) -> Option<ConfigValue> {
let v = self let v = self
.config .config
+181
View File
@@ -0,0 +1,181 @@
// <Kira Installer - universal Linux installer.>
// Copyright (C) <2026> <Kira Foundation>
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
/*
Actual installation script.
*/
///
/// So. I am goind to make dir structure lile this
/// /bzz/root
/// /bzz/home
/// Mount root from it and binmount home
///
use log;
use crate::{
kira_theming,
stage::{KiraConfig, StageAction, StageResult},
};
use iced::{Alignment, Task, futures, widget};
use rust_i18n::t;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum InstallationState {
UserConfirmDialog,
Partitionning,
CopyFiles,
Configuring,
Finish,
Error,
}
#[derive(Debug, Clone, PartialEq)]
pub struct InstallStage {
state: InstallationState,
progress: f32,
progress_message: String,
}
#[derive(Debug, Clone)]
pub enum Message {
UserConfirms,
UserDenies,
StartPartitioning,
UpdateProgress(Result<(f32, String), String>),
PartitioningFinish(Result<(), String>),
Back,
Cancel,
Finish,
}
#[derive(Debug)]
pub enum Update {
StgAct(StageAction),
Task(Task<Message>),
}
impl InstallStage {
pub fn new(config: &KiraConfig) -> Self {
Self {
state: InstallationState::UserConfirmDialog,
progress: 0.0,
progress_message: String::new(),
}
}
fn gen_result(&self) -> StageResult {
StageResult::new("install")
}
pub fn update(&mut self, message: Message) -> Update {
match message {
Message::UserDenies => Update::StgAct(StageAction::Back),
Message::UserConfirms => {
// starting installation process here
self.state = InstallationState::Partitionning;
Update::Task(Task::run(disk_part::check_connected(), Message::UpdateProgress))
//Update::Task(Task::perform(, Message::PartitioningFinish))
}
Message::StartPartitioning => Update::StgAct(StageAction::None),
Message::PartitioningFinish(res) => Update::StgAct(StageAction::None),
Message::UpdateProgress(maybe_msg) => {
match maybe_msg {
Ok((progress, msg))=> {
self.progress = progress;
self.progress_message = msg;
Update::StgAct(StageAction::None)
}
Err(ex) => {
log::error!("{}", &ex);
self.state = InstallationState::Error;
Update::StgAct(StageAction::None)
}
}
//Update::StgAct(StageAction::None)
},
Message::Cancel => Update::StgAct(StageAction::Abort),
Message::Back => Update::StgAct(StageAction::Back),
Message::Finish => Update::StgAct(StageAction::None),
}
}
pub fn view(&self) -> iced::Element<'_, Message> {
let action_button = if self.state == InstallationState::Finish {
widget::button(widget::text(t!("button.finish"))).on_press(Message::Finish)
} else {
widget::button(widget::text(t!("button.cancel"))).on_press(Message::Cancel)
};
// Embed the image bytes into the executable
let welcom_logo_handle = widget::image::Handle::from_bytes(kira_theming::get_logo_bytes());
let main_content = widget::column![
widget::container(
widget::column![
widget::image(welcom_logo_handle)
.width(iced::Pixels(128.0))
.height(iced::Pixels(128.0)),
widget::text(t!("install.caption")),
widget::progress_bar(0.0..=100.0, self.progress),
widget::text(self.progress_message.clone()),
]
.padding(10)
.spacing(10)
.align_x(Alignment::Center)
)
.height(iced::Length::Fill)
.width(iced::Length::Fill)
.align_x(Alignment::Center)
.align_y(Alignment::Center),
widget::container(action_button)
.width(iced::Length::Fill)
.align_y(Alignment::End)
.padding(10),
]
.align_x(Alignment::Center)
.spacing(10);
let page_stack = widget::Stack::with_capacity(2).push(main_content);
if self.state == InstallationState::UserConfirmDialog {
page_stack
.push(
widget::column![
widget::text(t!("install.warning")),
widget::row![
widget::button(widget::text(t!("button.no")))
.on_press(Message::UserDenies),
widget::space::horizontal(), // Pushes the right button to the far right
widget::button(widget::text(t!("button.yes")))
.on_press(Message::UserConfirms)
]
.width(iced::Length::Fill)
.align_y(Alignment::End)
.padding(10)
]
.align_x(Alignment::Center),
)
.into()
} else {
page_stack.into()
}
}
}
+2 -1
View File
@@ -18,6 +18,7 @@
This is Keyboar stage, used to select Keyboard Layouts This is Keyboar stage, used to select Keyboard Layouts
*/ */
use log;
use crate::kira_scroll_list::{self, ListViewMessage}; use crate::kira_scroll_list::{self, ListViewMessage};
use crate::stage::{StageAction, StageResult}; use crate::stage::{StageAction, StageResult};
use iced::{Alignment, Length, widget}; use iced::{Alignment, Length, widget};
@@ -254,7 +255,7 @@ impl KeyboardStage {
} }
} }
Err(ex) => { Err(ex) => {
println!( log::error!(
"Exception while trying to get kayboard layouts data from system {}", "Exception while trying to get kayboard layouts data from system {}",
ex ex
); );
-7
View File
@@ -156,8 +156,6 @@ impl LocaleStage {
let raw_loc_codes = get_locales_codes_list_blocking().unwrap(); let raw_loc_codes = get_locales_codes_list_blocking().unwrap();
let raw_loc_descr = get_locales_description_blocking().unwrap(); let raw_loc_descr = get_locales_description_blocking().unwrap();
//println!("{:?}", raw_loc_descr);
let locales: Vec<LocaleData> = raw_loc_codes let locales: Vec<LocaleData> = raw_loc_codes
.iter() .iter()
.filter_map(|(code, _)| { .filter_map(|(code, _)| {
@@ -187,11 +185,6 @@ impl LocaleStage {
LocaleData::default() LocaleData::default()
}; };
//println!("lang_match {:?}", lang_match);
//println!("loc_codes {:?}", raw_loc_codes);
// let locale = locales.iter().find(|l| l.code == lang_match.0).cloned();
let lang_locale_text = format!( let lang_locale_text = format!(
"{}: {}", "{}: {}",
t!("locale.select_language_locale"), t!("locale.select_language_locale"),
+2
View File
@@ -22,3 +22,5 @@ pub mod timezone;
pub mod locale; pub mod locale;
pub mod keyboard; pub mod keyboard;
pub mod partition; pub mod partition;
pub mod security;
pub mod install;
+5 -4
View File
@@ -19,6 +19,7 @@
it needed for installation to process. it needed for installation to process.
*/ */
use log;
use iced::{Alignment, Task, widget}; use iced::{Alignment, Task, widget};
use iced_moving_picture::widget::apng; use iced_moving_picture::widget::apng;
use rust_i18n::t; use rust_i18n::t;
@@ -81,7 +82,7 @@ fn check_connection_blocking() -> CheckResult {
} }
} }
Err(ex) => { Err(ex) => {
println!("Exception while trying to chen net connectivity: {}", ex); log::error!("Exception while trying to chen net connectivity: {}", ex);
CheckResult::CheckError CheckResult::CheckError
} }
} }
@@ -103,10 +104,10 @@ impl NetworkStage {
.and_then(|v| v.get("reqire_network")) .and_then(|v| v.get("reqire_network"))
.and_then(|v| v.as_bool()) .and_then(|v| v.as_bool())
{ {
println!("Config value reqire_network read {}", reqire_network_conf); log::debug!("Config value reqire_network read {}", reqire_network_conf);
reqire_network = reqire_network_conf; reqire_network = reqire_network_conf;
} else { } else {
println!("Error parsing network config. Using default values."); log::error!("Error parsing network config. Using default values.");
} }
let spinner_frames = let spinner_frames =
@@ -118,7 +119,7 @@ impl NetworkStage {
logo_handle: logo_handle, logo_handle: logo_handle,
internet_active: false, internet_active: false,
state: State::Cheking, state: State::Cheking,
use_net_settings: false, use_net_settings: true,
reqire_network: reqire_network, reqire_network: reqire_network,
spinner_frames: spinner_frames, spinner_frames: spinner_frames,
status_message: t!("network.checking").into(), status_message: t!("network.checking").into(),
+153 -62
View File
@@ -18,13 +18,15 @@
This is partition stage! This is partition stage!
*/ */
use crate::stage::{StageAction, StageResult}; use std::char;
use crate::kira_color_bar; use crate::kira_color_bar;
use crate::kira_disk_layout::{BlkDev, FSType, PartInfo, PartLayout}; use crate::kira_disk_layout::{BlkDev, FSType, PartInfo, PartLayout};
use crate::kira_size::KiraSize; use crate::kira_size::KiraSize;
use crate::kira_sysinfo; use crate::kira_sysinfo;
use iced::{Alignment, Color, widget}; use crate::stage::{StageAction, StageResult};
use iced::{Alignment, Color, Element, Length, widget};
use log;
use rust_i18n::t; use rust_i18n::t;
use toml::Table; use toml::Table;
@@ -33,6 +35,7 @@ pub enum SwapMode {
NoSwap, NoSwap,
SwapNoHibernate, SwapNoHibernate,
SwapHibernate, SwapHibernate,
SwapCustom,
} }
impl std::fmt::Display for SwapMode { impl std::fmt::Display for SwapMode {
@@ -41,14 +44,16 @@ impl std::fmt::Display for SwapMode {
Self::NoSwap => f.write_str(t!("partition.no_swap").as_ref()), Self::NoSwap => f.write_str(t!("partition.no_swap").as_ref()),
Self::SwapNoHibernate => f.write_str(t!("partition.swap_no_hibernate").as_ref()), Self::SwapNoHibernate => f.write_str(t!("partition.swap_no_hibernate").as_ref()),
Self::SwapHibernate => f.write_str(t!("partition.swap_hibernate").as_ref()), Self::SwapHibernate => f.write_str(t!("partition.swap_hibernate").as_ref()),
Self::SwapCustom => f.write_str(t!("partition.swap_custom").as_ref()),
} }
} }
} }
const SWAP_MODES: [SwapMode; 3] = [ const SWAP_MODES: [SwapMode; 4] = [
SwapMode::NoSwap, SwapMode::NoSwap,
SwapMode::SwapNoHibernate, SwapMode::SwapNoHibernate,
SwapMode::SwapHibernate, SwapMode::SwapHibernate,
SwapMode::SwapCustom,
]; ];
fn part_type_to_color(t: &FSType) -> Color { fn part_type_to_color(t: &FSType) -> Color {
@@ -63,6 +68,8 @@ fn part_type_to_color(t: &FSType) -> Color {
} }
} }
/// Converts PartInfo into data for ColorBar widget
///
fn part_to_ct_params(part: &PartInfo, dev_size: KiraSize) -> (String, u16, Color) { fn part_to_ct_params(part: &PartInfo, dev_size: KiraSize) -> (String, u16, Color) {
let fill_ratio: u16 = ((part.size.b() as f32 / dev_size.b() as f32) * 11.0) as u16; let fill_ratio: u16 = ((part.size.b() as f32 / dev_size.b() as f32) * 11.0) as u16;
let fill_ratio = fill_ratio.max(1); let fill_ratio = fill_ratio.max(1);
@@ -77,6 +84,10 @@ pub struct PartitionStage {
swap_mode: Option<SwapMode>, swap_mode: Option<SwapMode>,
use_zram: bool, use_zram: bool,
secure_boot: bool, secure_boot: bool,
encrypt_drive: bool,
secure_boot_in_setup_mode: bool,
custom_swap_size_mb: u32,
custom_swap_size_str: String,
selected_disk_parts: Option<Vec<(String, u16, Color)>>, selected_disk_parts: Option<Vec<(String, u16, Color)>>,
generated_disk_parts: Option<Vec<(String, u16, Color)>>, generated_disk_parts: Option<Vec<(String, u16, Color)>>,
system_ram_size: KiraSize, system_ram_size: KiraSize,
@@ -89,6 +100,10 @@ pub enum Message {
SelectSwapMode(SwapMode), SelectSwapMode(SwapMode),
ToggleZram(bool), ToggleZram(bool),
ToggleSecureBoot(bool), ToggleSecureBoot(bool),
ToggleEncrypDrive(bool),
SwapSizeFieldChange(String),
SwapSizeIncrement,
SwapSizeDecrement,
Next, Next,
Back, Back,
} }
@@ -96,7 +111,7 @@ pub enum Message {
impl PartitionStage { impl PartitionStage {
pub fn new(toml_config: &Table) -> Result<Self, String> { pub fn new(toml_config: &Table) -> Result<Self, String> {
let maybe_dev_list = BlkDev::list_sys_blk_dev(); let maybe_dev_list = BlkDev::list_sys_blk_dev();
log::info!("hiii from partition");
let min_disk_size_mb = toml_config let min_disk_size_mb = toml_config
.get("partition") .get("partition")
.and_then(|v| v.as_table()) .and_then(|v| v.as_table())
@@ -106,6 +121,14 @@ impl PartitionStage {
let min_disk_size: KiraSize = KiraSize::new_mb(min_disk_size_mb.try_into().unwrap_or(8192)); let min_disk_size: KiraSize = KiraSize::new_mb(min_disk_size_mb.try_into().unwrap_or(8192));
let sec_boot_setup = match kira_sysinfo::get_secure_boot_status() {
Ok(sb_status) => sb_status.setup_mode,
Err(ex) => {
log::error!("Unable to get secure boot status! {}", ex);
false
}
};
match maybe_dev_list { match maybe_dev_list {
Ok(dev_list) => { Ok(dev_list) => {
let acepted_dev: Vec<BlkDev> = dev_list let acepted_dev: Vec<BlkDev> = dev_list
@@ -116,15 +139,16 @@ impl PartitionStage {
// getting system memory info // getting system memory info
let system_ram_size = KiraSize::new_kb(kira_sysinfo::get_meminfo()["MemTotal"]); // from kB to bytes let system_ram_size = KiraSize::new_kb(kira_sysinfo::get_meminfo()["MemTotal"]); // from kB to bytes
let vram_of_all_gpus_size = KiraSize::new( let vram_of_all_gpus_size = KiraSize::new_b(
kira_sysinfo::get_gpus_list() kira_sysinfo::get_gpus_list()
.iter() .iter()
.fold(0u64, |acc, gpi| acc + gpi.vis_vram_total), .fold(0u64, |acc, gpi| acc + gpi.vis_vram_total),
); );
println!( log::debug!(
"stage init ram {} vram {}", "stage init ram {} vram {}",
system_ram_size, vram_of_all_gpus_size system_ram_size,
vram_of_all_gpus_size
); );
Ok(Self { Ok(Self {
device: None, device: None,
@@ -132,6 +156,10 @@ impl PartitionStage {
swap_mode: Some(SwapMode::SwapNoHibernate), swap_mode: Some(SwapMode::SwapNoHibernate),
use_zram: true, use_zram: true,
secure_boot: false, secure_boot: false,
encrypt_drive: false,
secure_boot_in_setup_mode: sec_boot_setup,
custom_swap_size_mb: 1024,
custom_swap_size_str: "1024MB".into(),
selected_disk_parts: None, selected_disk_parts: None,
generated_disk_parts: None, generated_disk_parts: None,
system_ram_size: system_ram_size, system_ram_size: system_ram_size,
@@ -143,11 +171,17 @@ impl PartitionStage {
} }
fn get_swap_size(&self) -> KiraSize { fn get_swap_size(&self) -> KiraSize {
if let Some(s_mode) = self.swap_mode
&& s_mode == SwapMode::SwapCustom
{
return KiraSize::new_mb(self.custom_swap_size_mb as u64);
}
let total_ram = self.system_ram_size + self.vram_of_all_gpus_size; let total_ram = self.system_ram_size + self.vram_of_all_gpus_size;
println!("total_ram {}", total_ram);
let sys_ram_gb = self.system_ram_size.gb(); let sys_ram_gb = self.system_ram_size.gb();
println!("sys_ram_gb {}", sys_ram_gb); log::debug!("sys_ram_gb {}", sys_ram_gb);
let disk_swap_gb = if sys_ram_gb >= 32 { let mut disk_swap_gb = if sys_ram_gb >= 32 {
1u64 1u64
} else if sys_ram_gb >= 24 { } else if sys_ram_gb >= 24 {
2u64 2u64
@@ -159,26 +193,41 @@ impl PartitionStage {
sys_ram_gb * 2 sys_ram_gb * 2
}; };
println!("disk_swap_gb {}", disk_swap_gb); if self.use_zram {
disk_swap_gb = disk_swap_gb.saturating_sub(sys_ram_gb).max(2);
}
let disk_swap = KiraSize::new_gb(disk_swap_gb); let disk_swap = KiraSize::new_gb(disk_swap_gb);
println!("disk_swap {}", disk_swap);
match self.swap_mode { match self.swap_mode {
Some(SwapMode::NoSwap) => KiraSize::new(0), Some(SwapMode::NoSwap) => KiraSize::new_b(0),
Some(SwapMode::SwapHibernate) => disk_swap + total_ram, Some(SwapMode::SwapHibernate) => disk_swap + total_ram,
Some(SwapMode::SwapNoHibernate) => disk_swap, Some(SwapMode::SwapNoHibernate) => disk_swap,
_ => KiraSize::new_gb(2), _ => KiraSize::new_gb(2),
} }
} }
fn gen_layout(&mut self) {
if let Some(dev) = self.device.clone() {
let swap_size: KiraSize = self.get_swap_size();
self.generated_disk_parts = Some(
PartLayout::gen_classic_layout(dev.clone(), KiraSize::new_mb(512), swap_size)
.part_list
.iter()
.map(|part_info| part_to_ct_params(part_info, dev.size))
.collect(),
);
}
}
fn gen_result(&self) -> StageResult { fn gen_result(&self) -> StageResult {
let swap_size: KiraSize = self.get_swap_size();
let dev_name = self
.device
.clone()
.expect("Device should be selected!")
.name;
StageResult::new("partition") StageResult::new("partition")
.add_val_string( .add_val_string("device", dev_name)
"device",
self.device
.as_ref()
.and_then(|v| Some(v.name.clone()))
.unwrap_or_else(|| "NONE".to_string()),
)
.add_val_string( .add_val_string(
"swap_mode", "swap_mode",
self.swap_mode self.swap_mode
@@ -186,8 +235,10 @@ impl PartitionStage {
.unwrap_or(SwapMode::NoSwap) .unwrap_or(SwapMode::NoSwap)
.to_string(), .to_string(),
) )
.add_val_bool("zram", self.use_zram) .add_val_u64("swap_size_mb", swap_size.mb())
.add_val_bool("use_zram", self.use_zram)
.add_val_bool("secure_boot", self.secure_boot) .add_val_bool("secure_boot", self.secure_boot)
.add_val_bool("encrypt_drive", self.encrypt_drive)
} }
pub fn update(&mut self, message: Message) -> StageAction { pub fn update(&mut self, message: Message) -> StageAction {
@@ -202,7 +253,7 @@ impl PartitionStage {
.collect(), .collect(),
), ),
Err(ex) => { Err(ex) => {
println!("Error getting dev partitions info: {}", ex); log::error!("Error getting dev partitions info: {}", ex);
None None
} }
}; };
@@ -210,7 +261,7 @@ impl PartitionStage {
let swap_size: KiraSize = self.get_swap_size(); let swap_size: KiraSize = self.get_swap_size();
self.generated_disk_parts = Some( self.generated_disk_parts = Some(
PartLayout::gen_classic_layout(dev.clone(), KiraSize::new_mb(512), swap_size) PartLayout::gen_classic_layout(dev.clone(), KiraSize::new_mb(128), swap_size)
.part_list .part_list
.iter() .iter()
.map(|part_info| part_to_ct_params(part_info, dev.size)) .map(|part_info| part_to_ct_params(part_info, dev.size))
@@ -219,86 +270,126 @@ impl PartitionStage {
self.device = Some(dev); self.device = Some(dev);
StageAction::None StageAction::None
} },
Message::SelectSwapMode(sm) => { Message::SelectSwapMode(sm) => {
self.swap_mode = Some(sm); self.swap_mode = Some(sm);
self.gen_layout();
if let Some(dev) = self.device.clone() {
let swap_size: KiraSize = self.get_swap_size();
self.generated_disk_parts = Some(
PartLayout::gen_classic_layout(
dev.clone(),
KiraSize::new_mb(512),
swap_size,
)
.part_list
.iter()
.map(|part_info| part_to_ct_params(part_info, dev.size))
.collect(),
);
}
StageAction::None StageAction::None
} },
Message::ToggleSecureBoot(t) => { Message::ToggleSecureBoot(t) => {
self.secure_boot = t; self.secure_boot = t;
StageAction::None StageAction::None
} },
Message::ToggleEncrypDrive(t) => {
self.encrypt_drive = t;
StageAction::None
},
Message::ToggleZram(t) => { Message::ToggleZram(t) => {
self.use_zram = t; self.use_zram = t;
self.gen_layout();
StageAction::None StageAction::None
} },
Message::SwapSizeIncrement => {
self.custom_swap_size_mb = (self.custom_swap_size_mb / 1024u32)
.strict_mul(1024u32)
.saturating_add(1024u32);
self.custom_swap_size_str = format!("{}MB", self.custom_swap_size_mb);
self.gen_layout();
StageAction::None
},
Message::SwapSizeDecrement => {
self.custom_swap_size_mb = (self.custom_swap_size_mb / 1024u32)
.strict_mul(1024u32)
.saturating_sub(1024u32);
self.custom_swap_size_str = format!("{}MB", self.custom_swap_size_mb);
self.gen_layout();
StageAction::None
},
Message::SwapSizeFieldChange(f_v) => {
let mut only_num = f_v.trim().to_string();
only_num.retain(char::is_numeric);
self.custom_swap_size_mb = u32::from_str_radix(&only_num, 10).unwrap_or(1024);
self.custom_swap_size_str = format!("{}MB", self.custom_swap_size_mb);
self.gen_layout();
StageAction::None
},
Message::Back => StageAction::Back, Message::Back => StageAction::Back,
Message::Next => StageAction::Next(self.gen_result()), Message::Next => StageAction::Next(self.gen_result()),
} }
} }
pub fn view(&self) -> iced::Element<'_, Message> { pub fn view(&self) -> iced::Element<'_, Message> {
let swap_size_select: Element<'_, _> = match self.swap_mode {
Some(SwapMode::SwapCustom) => widget::row![
widget::text_input("", &self.custom_swap_size_str)
.width(100)
.on_input(Message::SwapSizeFieldChange),
widget::button("+").on_press(Message::SwapSizeIncrement),
widget::button("-").on_press(Message::SwapSizeDecrement),
]
.spacing(2)
.into(),
_ => widget::space().into(),
};
let dev_parts_text = match self.generated_disk_parts { let dev_parts_text = match self.generated_disk_parts {
Some(_) => widget::text(t!("partition.current_after_install")), Some(_) => widget::text(t!("partition.current_after_install")),
None => widget::text(""), None => widget::text(""),
}; };
let dev_parts_content = kira_color_bar::color_bar(self.generated_disk_parts.clone(), 56); let dev_parts_content = kira_color_bar::color_bar(self.generated_disk_parts.clone(), 64);
let dev_parts_content = widget::column![dev_parts_text, dev_parts_content]; let dev_parts_content = widget::column![dev_parts_text, dev_parts_content];
let back_button = widget::button(widget::text(t!("button.back"))).on_press(Message::Back); let back_button = widget::button(widget::text(t!("button.back"))).on_press(Message::Back);
let next_button = widget::button(widget::text(t!("button.next"))).on_press(Message::Next); let next_button = if self.device.is_some() {
widget::button(widget::text(t!("button.next"))).on_press(Message::Next)
} else {
widget::button(widget::text(t!("button.next")))
};
let selected_dev_text = match self.selected_disk_parts { let selected_dev_text = match self.selected_disk_parts {
Some(_) => widget::text(t!("partition.current_dev_layout")), Some(_) => widget::text(t!("partition.current_dev_layout")),
None => widget::text(""), None => widget::text(""),
}; };
let selected_dev_content = kira_color_bar::color_bar(self.selected_disk_parts.clone(), 56); let selected_dev_content = kira_color_bar::color_bar(self.selected_disk_parts.clone(), 64);
let selected_dev_content = widget::column![selected_dev_text, selected_dev_content]; let selected_dev_content = widget::column![selected_dev_text, selected_dev_content];
let use_sec_boot_checkbox = if self.secure_boot_in_setup_mode {
widget::checkbox(self.secure_boot).label(t!("partition.use_secure_boot"))
.on_toggle(Message::ToggleSecureBoot)
}
else {widget::checkbox(self.secure_boot).label(t!("partition.use_secure_boot"))};
widget::column![ widget::column![
widget::container( widget::container(
widget::column![ widget::column![
widget::row![ widget::rule::horizontal(2),
widget::pick_list( widget::text(t!("partition.select_device")),
self.devices.clone(), widget::pick_list(
self.device.clone(), self.devices.clone(),
Message::SelectDevice, self.device.clone(),
) Message::SelectDevice,
.placeholder(t!("partition.select_device_placeholder")), )
widget::text(t!("partition.select_device")) .placeholder(t!("partition.select_device_placeholder")),
] widget::rule::horizontal(2),
.spacing(5) widget::text(t!("partition.select_swapmode")),
.align_y(Alignment::Center),
widget::row![ widget::row![
widget::pick_list(SWAP_MODES, self.swap_mode, Message::SelectSwapMode,) widget::pick_list(SWAP_MODES, self.swap_mode, Message::SelectSwapMode,)
.placeholder(t!("partition.select_swapmode_placeholder")), .placeholder(t!("partition.select_swapmode_placeholder")),
widget::text(t!("partition.select_swapmode")) swap_size_select
] ]
.spacing(5) .spacing(5)
.width(Length::Shrink)
.align_y(Alignment::Center), .align_y(Alignment::Center),
widget::checkbox(self.use_zram) widget::checkbox(self.use_zram)
.label(t!("partition.use_zram")) .label(t!("partition.use_zram"))
.on_toggle(Message::ToggleZram), .on_toggle(Message::ToggleZram),
widget::checkbox(self.secure_boot) widget::rule::horizontal(2),
.label(t!("partition.use_secure_boot")) use_sec_boot_checkbox,
.on_toggle(Message::ToggleSecureBoot), widget::rule::horizontal(2),
widget::checkbox(self.encrypt_drive)
.label(t!("partition.encrypt_drive"))
.on_toggle(Message::ToggleEncrypDrive),
widget::rule::horizontal(2),
selected_dev_content, selected_dev_content,
dev_parts_content, dev_parts_content,
] ]
+290
View File
@@ -0,0 +1,290 @@
// <Kira Installer - universal Linux installer.>
// Copyright (C) <2026> <Kira Foundation>
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
/*
This is Security stage, setup users logins and such
*/
use crate::stage::{StageAction, StageResult};
use iced::{Alignment, Color, widget};
use rust_i18n::t;
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
enum PasswordStrenght {
Low,
Middle,
Hight,
}
impl PasswordStrenght {
pub fn from_password(password: &str) -> Self {
if password.len() < 8 {
Self::Low
} else if password.len() < 16 {
Self::Middle
} else {
Self::Hight
}
}
pub fn color(&self) -> Color {
match self {
Self::Low => Color::from_rgb8(212, 32, 32),
Self::Middle => Color::from_rgb8(212, 212, 32),
Self::Hight => Color::from_rgb8(32, 212, 32),
}
}
}
impl std::fmt::Display for PasswordStrenght {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Low => f.write_str(t!("security.pass_low").as_ref()),
Self::Middle => f.write_str(t!("security.pass_middle").as_ref()),
Self::Hight => f.write_str(t!("security.pass_hight").as_ref()),
}
}
}
#[derive(Debug, Clone)]
pub struct SecurityStage {
user_name: String,
user_full_name: String,
user_password: String,
root_password: String,
reuse_user_password_for_roor: bool,
user_pass_is_secure: bool,
root_pass_is_secure: bool,
user_password_strenght: PasswordStrenght,
root_password_strenght: PasswordStrenght,
}
#[derive(Debug, Clone)]
pub enum Message {
ToggleReusePass(bool),
UserNameChange(String),
UserFullNameChange(String),
UserPasswordChange(String),
RootPasswordChange(String),
ToggleUserPassSecure,
ToggleRootPassSecure,
Next,
Back,
}
/// Mothod safely retain first n chars in utf-8 string avoiding cutting utf-8 char in the middle
fn retain_first_n(s: &str, n: usize) -> &str {
s.char_indices()
.nth(n)
.and_then(|(idx, _)| Some(&s[..idx]))
.unwrap_or(s)
}
impl SecurityStage {
pub fn new() -> Self {
Self {
user_name: "".into(),
user_full_name: "".into(),
user_password: "".into(),
root_password: "".into(),
reuse_user_password_for_roor: false,
user_pass_is_secure: true,
root_pass_is_secure: true,
user_password_strenght: PasswordStrenght::Low,
root_password_strenght: PasswordStrenght::Low,
}
}
fn gen_result(&self) -> StageResult {
StageResult::new("security")
.add_val_string("user_name", self.user_name.clone())
.add_val_string("user_full_name", self.user_full_name.clone())
.add_val_string("user_full_name", self.user_full_name.clone())
.add_val_string("user_password", self.user_password.clone())
.add_val_string("root_password", self.root_password.clone())
}
pub fn update(&mut self, message: Message) -> StageAction {
match message {
Message::ToggleReusePass(reuse_pass) => {
self.reuse_user_password_for_roor = reuse_pass;
if reuse_pass {
self.root_password = self.user_password.clone();
}
StageAction::None
}
Message::UserNameChange(u_name) => {
let mut trimmed = u_name
.trim_start_matches(|c: char| !c.is_ascii_alphabetic())
.to_string();
trimmed.retain(|c| c.is_ascii_alphanumeric() || c == '-');
trimmed.truncate(30); // it is safe bacuse we retained only ascii characters
self.user_name = trimmed;
StageAction::None
}
Message::UserFullNameChange(f_name) => {
let mut res = f_name.clone();
res.retain(|c| c.is_alphanumeric() || ['_', '-', ' ', '.', ','].contains(&c));
self.user_full_name = retain_first_n(&res, 128).to_string();
StageAction::None
}
Message::UserPasswordChange(u_pass) => {
let mut res = u_pass;
res.retain(|c: char| !c.is_control());
self.user_password = res;
self.user_password_strenght = PasswordStrenght::from_password(&self.user_password);
if self.reuse_user_password_for_roor {
self.root_password_strenght = self.user_password_strenght;
self.root_password = self.user_password.clone();
}
StageAction::None
}
Message::RootPasswordChange(r_pass) => {
let mut res: String = r_pass;
res.retain(|c: char| !c.is_control());
self.root_password = res;
self.root_password_strenght = PasswordStrenght::from_password(&self.root_password);
StageAction::None
}
Message::ToggleUserPassSecure => {
self.user_pass_is_secure = !self.user_pass_is_secure;
StageAction::None
}
Message::ToggleRootPassSecure => {
self.root_pass_is_secure = !self.root_pass_is_secure;
StageAction::None
}
Message::Back => StageAction::Back,
Message::Next => StageAction::Next(self.gen_result()),
}
}
pub fn view(&self) -> iced::Element<'_, Message> {
let back_button = widget::button(widget::text(t!("button.back"))).on_press(Message::Back);
let next_button = if self.user_name.is_empty()
|| self.user_password.is_empty()
|| self.root_password.is_empty()
{
widget::button(widget::text(t!("button.next")))
} else {
widget::button(widget::text(t!("button.next"))).on_press(Message::Next)
};
let user_pass_sec_text = if self.user_pass_is_secure {
widget::text("👁")
} else {
widget::text("*")
};
let root_pass_sec_text = if self.root_pass_is_secure {
widget::text("👁")
} else {
widget::text("*")
};
let user_pass_sec_text = user_pass_sec_text
.width(12)
.align_x(Alignment::Center)
.align_y(Alignment::Center);
let root_pass_sec_text = root_pass_sec_text
.width(12)
.align_x(Alignment::Center)
.align_y(Alignment::Center);
let mut root_pass_input =
widget::text_input("", &self.root_password).secure(self.root_pass_is_secure);
if !self.reuse_user_password_for_roor {
root_pass_input = root_pass_input.on_input(Message::RootPasswordChange);
}
let user_p_strenght_text = widget::text(self.user_password_strenght.to_string())
.color(self.user_password_strenght.color());
let root_p_strenght_text = widget::text(self.root_password_strenght.to_string())
.color(self.root_password_strenght.color());
widget::column![
widget::container(
widget::column![
widget::column![
widget::text(t!("security.user_name")),
widget::text_input("", &self.user_name)
.width(256)
.on_input(Message::UserNameChange)
],
widget::column![
widget::text(t!("security.user_full_name")),
widget::text_input("", &self.user_full_name)
.width(256)
.on_input(Message::UserFullNameChange)
],
widget::column![
widget::text(t!("security.user_password")),
widget::row![
widget::text_input("", &self.user_password)
.width(512)
.on_input(Message::UserPasswordChange)
.secure(self.user_pass_is_secure),
widget::button(user_pass_sec_text)
.on_press(Message::ToggleUserPassSecure),
widget::container(user_p_strenght_text)
.align_x(Alignment::Center)
.style(widget::container::bordered_box)
.style(widget::container::rounded_box)
.padding(6)
.width(160),
]
.spacing(5)
.align_y(Alignment::Center)
],
widget::rule::horizontal(2),
widget::column![
widget::text(t!("security.root_password")),
widget::row![
root_pass_input.width(512),
widget::button(root_pass_sec_text)
.on_press(Message::ToggleRootPassSecure),
widget::container(root_p_strenght_text)
.align_x(Alignment::Center)
.style(widget::container::rounded_box)
.style(widget::container::bordered_box)
.padding(6)
.width(160),
]
.spacing(5)
.align_y(Alignment::Center),
widget::checkbox(self.reuse_user_password_for_roor)
.label(t!("security.reuse_user_password"))
.on_toggle(Message::ToggleReusePass),
],
]
.padding(10)
.spacing(10)
.align_x(Alignment::Center)
)
.height(iced::Length::Fill)
.width(iced::Length::Fill)
.align_x(Alignment::Center)
.align_y(Alignment::Center),
widget::row![
back_button,
widget::space::horizontal(), // Pushes the right button to the far right
next_button,
]
.width(iced::Length::Fill)
.align_y(Alignment::End)
.padding(10),
]
.align_x(Alignment::Center)
.spacing(10)
.into()
}
}
+2 -1
View File
@@ -18,6 +18,7 @@
This is TimeZone stage, used to select timezone This is TimeZone stage, used to select timezone
*/ */
use log;
use crate::stage::{StageAction, StageResult}; use crate::stage::{StageAction, StageResult};
use iced::{Alignment, Length, widget}; use iced::{Alignment, Length, widget};
use rust_i18n::t; use rust_i18n::t;
@@ -127,7 +128,7 @@ impl TimeZoneStage {
} }
} }
Err(ex) => { Err(ex) => {
println!( log::error!(
"Exception while trying to get time zones list from system {}", "Exception while trying to get time zones list from system {}",
ex ex
); );