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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
use super::*;
use time::{Duration, Instant};

/// The cursor structure used in the terminal.
/// A cursor is a special symbol shown in the text box of a terminal. It indicates the position of character where the next input would be put or the delete operation works on.
/// Terminal invokes its `display` method in a loop to let a cursor blink.
pub struct Cursor {
    /// Whether the cursor is enabled in the terminal.
    enabled: bool,
    /// The blinking frequency.
    freq: Duration,
    /// The last time it blinks.
    time: Instant,
    /// The current blinking state show/hidden
    show: bool,
    /// The color of the cursor
    color: Color,
    /// The position of the cursor relative to the end of terminal text in number of characters.
    pub offset_from_end: usize,
    /// The underlying character at the position of the cursor.
    /// It is shown when the cursor is unseen.
    pub underlying_char: u8,
}

impl Cursor {
    /// Reset the state of the cursor as unseen
    pub fn reset(&mut self) {
        self.show = true;
        self.time = Instant::now();
    }

    /// Enable a cursor
    pub fn enable(&mut self) {
        self.enabled = true;
        self.reset();
    }

    /// Disable a cursor
    pub fn disable(&mut self) {
        self.enabled = false;
    }

    /// Let a cursor blink. It is invoked in a loop.
    pub fn blink(&mut self) -> bool {
        if self.enabled {
            let time = Instant::now();

            if time >= self.time + self.freq {
                self.time = time;
                self.show = !self.show;
                return true;
            }
        }
        true
    }

    /// Whether a cursor is seen
    pub fn show(&self) -> bool {
        self.enabled && self.show
    }

    /// Display a cursor in a framebuffer
    /// # Arguments
    /// * `coordinate`: the start point of a textarea in the framebuffer.
    /// * `column`: the column of the cursor in the textarea.
    /// * `line`: the line of the cursor in the textarea.
    /// * `framebuffer`: the framebuffer to display the cursor in.
    ///
    /// Returns a bounding box which wraps the cursor.
    pub fn display<P: Pixel>(
        &mut self,
        coordinate: Coord,
        column: usize,
        line: usize,
        framebuffer: &mut Framebuffer<P>,
    ) -> Result<Rectangle, &'static str> where Color: Into<P> {
        if self.blink() {
            if self.show() {
                framebuffer_drawer::fill_rectangle(
                    framebuffer,
                    coordinate
                        + (
                            (column * CHARACTER_WIDTH) as isize,
                            (line * CHARACTER_HEIGHT) as isize,
                        )
                        + (0, 1),
                    CHARACTER_WIDTH,
                    CHARACTER_HEIGHT - 2,
                    self.color.into(),
                );
            } else {
                framebuffer_printer::print_ascii_character(
                    framebuffer,
                    self.underlying_char,
                    FONT_FOREGROUND_COLOR.into(),
                    FONT_BACKGROUND_COLOR.into(),
                    coordinate,
                    column,
                    line,
                )
            }
        }

        let top_left = coordinate
            + (
                (column * CHARACTER_WIDTH) as isize,
                (line * CHARACTER_HEIGHT) as isize,
            );
        let bounding_box = Rectangle {
            top_left,
            bottom_right: top_left + (CHARACTER_WIDTH as isize, CHARACTER_HEIGHT as isize),
        };

        Ok(bounding_box)
    }

    /// Sets the position of the cursor relative to the end of the command
    pub fn set_offset_from_end(&mut self, offset: usize) {
        self.offset_from_end = offset;
    }

    /// Gets the position of the cursor relative to the end of the command
    pub fn offset_from_end(&self) -> usize {
        self.offset_from_end
    }

    /// Sets the character at the position of the cursor
    pub fn set_underlying_char(&mut self, c: u8) {
        self.underlying_char = c;
    }

    /// Gets the character at the position of the cursor
    pub fn underlying_char(&self) -> u8 {
        self.underlying_char
    }
}

impl Default for Cursor  {
    fn default() -> Self {
        Cursor {
            enabled: true,
            freq: DEFAULT_CURSOR_FREQ,
            time: Instant::now(),
            show: true,
            color: FONT_FOREGROUND_COLOR,
            offset_from_end: 0,
            underlying_char: 0,
        }
    }
}