/*
* bb
*
* Copyright 2019 Manos Pitsidianakis
*
* This file is part of bb.
*
* bb 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.
*
* bb 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 bb. If not, see <http://www.gnu.org/licenses/>.
*/
/*! Various useful components that can be used in a generic fashion.
*/
extern crate cassowary;
use super::*;
use std::fs::File;
use std::io::prelude::*;
use std::str::FromStr;
use cassowary::strength::{REQUIRED, STRONG, WEAK};
use cassowary::WeightedRelation::*;
use cassowary::{Solver, Variable};
#[derive(Debug)]
pub enum PageMovement {
Up,
Down,
Home,
PageUp,
PageDown,
End,
}
const KILOBYTE: f64 = 1024.0;
const MEGABYTE: f64 = KILOBYTE * 1024.0;
const GIGABYTE: f64 = MEGABYTE * 1024.0;
const PETABYTE: f64 = GIGABYTE * 1024.0;
pub struct Bytes(pub usize);
impl Bytes {
pub fn as_convenient_string(&self) -> String {
let bytes = self.0 0.0 {
"0".to_string()
} else if bytes < KILOBYTE {
format!("{:.2} bytes", bytes)
} else if bytes < MEGABYTE {
format!("{:.2} KiB", bytes / KILOBYTE)
} else if bytes < GIGABYTE {
format!("{:.2} MiB", bytes / MEGABYTE)
} else if bytes < PETABYTE {
format!("{:.2} GiB", bytes / GIGABYTE)
} else {
format!("{:.2} PiB", bytes / PETABYTE)
}
}
}
#[derive(Debug, Copy, Clone)]
pub struct Stat {
pub user_time: usize,
pub system_time: usize,
pub nice_time: usize,
pub idle_time: usize,
pub iowait_time: usize,
}
impl Stat {
pub fn total_time(&self) -> usize {
self.user_time + self.system_time + self.nice_time + self.idle_time + self.iowait_time
}
}
pub fn get_stat(boot_time: &mut usize) -> Vec<Stat> {
let mut file = File::open("/proc/stat").unwrap();
let mut res = String::with_capacity(2048);
file.read_to_string(&mut res).unwrap();
let mut lines_iter = res.lines();
let mut ret = Vec::with_capacity(8);
let mut line;
loop {
line = lines_iter.next().unwrap();
if !line.starts_with("cpu") {
break;
}
let mut mut_value_iter = line.split_whitespace().skip(1);
let user_time = usize::from_str(&mut_value_iter.next().unwrap()).unwrap();
/* skip nice time */
let nice_time = usize::from_str(&mut_value_iter.next().unwrap()).unwrap();
let system_time = usize::from_str(&mut_value_iter.next().unwrap()).unwrap();
let idle_time = usize::from_str(&mut_value_iter.next().unwrap()).unwrap();
let iowait_time = usize::from_str(&mut_value_iter.next().unwrap()).unwrap();
ret.push(Stat {
user_time,
system_time,
nice_time,
idle_time,
iowait_time,
});
}
while !line.starts_with("btime") {
line = lines_iter.next().unwrap();
}
*boot_time = usize::from_str(&line.split_whitespace().skip(1).next().unwrap()).unwrap();
ret
}
/// A horizontally split in half container.
#[derive(Debug)]
pub struct Window {
top_bars: Box<dyn Component>,
list: Box<dyn Component>,
}
impl fmt::Display for Window {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
Display::fmt(&self.top_bars, f)
}
}
impl Window {
pub fn new(top_bars: Box<dyn Component>, list: Box<dyn Component>) -> Self {
Window { top_bars, list }
}
}
struct Element {
top: Variable,
bottom: Variable,
}
impl Component for Window {
fn draw(
&mut self,
grid: &mut CellBuffer,
area: Area,
dirty_areas: &mut VecDeque<Area>,
tick: bool,
) {
if !is_valid_area!(area) {
return;
}
let upper_left = upper_left!(area);
let bottom_right = bottom_right!(area);
let total_rows = get_y(bottom_right) - get_y(upper_left);
let window_height = Variable::new();
let top_bars = Element {
top: Variable::new(),
bottom: Variable::new(),
};
let list = Element {
top: Variable::new(),
bottom: Variable::new(),
};
let mut solver = Solver::new();
solver
.add_constraints(&[
window_height | GE(REQUIRED) | 0.0, // positive window height
top_bars.top | EQ(REQUIRED) | 0.0, // top align
list.bottom | EQ(REQUIRED) | window_height, // right align
list.top | GE(REQUIRED) | top_bars.bottom, // no overlap
// positive heights
top_bars.top | LE(REQUIRED) | top_bars.bottom,
list.top | LE(REQUIRED) | list.bottom,
// preferred heights:
top_bars.bottom - top_bars.top | GE(REQUIRED) | 6.0,
top_bars.bottom - top_bars.top | EQ(WEAK) | 8.0,
top_bars.bottom - top_bars.top | LE(REQUIRED) | 8.0,
list.bottom - list.top | GE(REQUIRED) | 11.0,
])
.unwrap();
solver.add_edit_variable(window_height, STRONG).unwrap();
solver
.suggest_value(window_height, total_rows as f64)
.unwrap();
let changes = solver.fetch_changes();
let mid = get_y(upper_left)
+ (*changes
.iter()
.find(|(a, _)| *a == top_bars.bottom)
.map(|(_, b)| b)
.unwrap() as usize);
self.top_bars.draw(
grid,
(
upper_left,
(get_x(bottom_right), get_y(upper_left) + mid - 1),
),
dirty_areas,
tick,
);
self.list.draw(
grid,
((get_x(upper_left), get_y(upper_left) + mid), bottom_right),
dirty_areas,
tick,
);
}
fn process_event(&mut self, event: &mut UIEvent) {
self.top_bars.process_event(event);
self.list.process_event(event);
}
fn is_dirty(&self) -> bool {
self.top_bars.is_dirty() || self.list.is_dirty()
}
fn set_dirty(&mut self) {
self.top_bars.set_dirty();
self.list.set_dirty();
}
fn get_shortcuts(&self) -> ShortcutMaps {
let mut top_bars_map = self.top_bars.get_shortcuts();
top_bars_map.extend(self.list.get_shortcuts().into_iter());
top_bars_map
}
}