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
use std::io::Write;

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Color (pub u8, pub u8, pub u8);

pub struct Terminal {
    width: usize,
    height: usize,

    ch: Box<[Box<[char]>]>,
    fg: Box<[Box<[Color]>]>,
    bg: Box<[Box<[Color]>]>
}

impl Terminal {
    pub fn new() -> Self {
        let mut win_size: libc::winsize = unsafe {std::mem::zeroed()};
        unsafe {libc::ioctl(libc::STDOUT_FILENO, libc::TIOCGWINSZ, &mut win_size)};

        let columns = win_size.ws_col as usize;
        let rows = win_size.ws_row as usize;


        let mut ch_row = Vec::new();
        ch_row.resize(columns, ' ');

        let mut ch_buf = Vec::new();
        ch_buf.resize(rows, ch_row.clone().into_boxed_slice());


        let mut color_row = Vec::new();
        color_row.resize(columns, Color(0, 0, 0));

        let mut fg_buf = Vec::new();
        fg_buf.resize(rows, color_row.clone().into_boxed_slice());

        let mut bg_buf = Vec::new();
        bg_buf.resize(rows, color_row.clone().into_boxed_slice());


        Self {
            width: columns,
            height: rows,
            ch: ch_buf.into_boxed_slice(),
            fg: fg_buf.into_boxed_slice(),
            bg: bg_buf.into_boxed_slice()
        }
    }

    pub fn set_char(&mut self, x: usize, y: usize, ch: char) {
        if !self.is_valid_coords(x, y) {return};
        self.ch[y][x] = ch;
    }

    pub fn set_color_char(&mut self, x: usize, y: usize, ch: char, fg: Color, bg: Color) {
        if !self.is_valid_coords(x, y) {return};
        self.ch[y][x] = ch;
        self.fg[y][x] = fg;
        self.bg[y][x] = bg;
    }

    pub fn set_fg(&mut self, x: usize, y: usize, color: Color) {
        if !self.is_valid_coords(x, y) {return};
        self.fg[y][x] = color;
    }

    pub fn set_bg(&mut self, x: usize, y: usize, color: Color) {
        if !self.is_valid_coords(x, y) {return};
        self.bg[y][x] = color;
    }

    pub fn clear(&mut self) {
        for y in 0..self.height {
            for x in 0..self.width {
                self.ch[y][x] = ' ';
                self.fg[y][x] = Color(0, 0, 0);
                self.bg[y][x] = Color(0, 0, 0);
            }
        }
    }

    pub fn display(&self) {
        let mut output = String::new();

        for y in 0..self.height {
            for x in 0..self.width {
                let fg = self.fg[y][x];
                let bg = self.bg[y][x];

                output += format!("\x1b[38;2;{};{};{}m\x1b[48;2;{};{};{}m{}", fg.0, fg.1, fg.2, bg.0, bg.1, bg.2, self.ch[y][x]).as_str();
            }
            if y < self.height-1 {output += "\n"};
        }

        print!("{}\x1b[0m", output.trim_end());

        let _ = std::io::stdout().flush();
    }

    fn is_valid_coords(&self, x: usize, y: usize) -> bool {
        x < self.width && y < self.height
    }
}