summaryrefslogtreecommitdiffstats
path: root/src/ui/components/utilities.rs
blob: 2828bc163252556882177307f1c61d4d486a004e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
pre { line-height: 125%; }
td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #ff
/*
 * 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
    }
}