/* * 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 . */ /*! 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 as f64; if bytes == 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 { 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, list: Box, } 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, list: Box) -> 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, 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 } }