console_ui_engine_null/
console.rs

1#[cfg(windows)] extern crate winapi;
2
3use std::io::{stdout, Write};
4use std::thread::sleep;
5use std::time::Duration;
6
7use crossterm::{cursor, ExecutableCommand, input, queue, Result, style, terminal};
8use crossterm::screen::RawScreen;
9use crossterm::terminal::{ClearType, size};
10
11use crate::buffer::SizedBuffer;
12use crate::input_events::InputEvents;
13use crate::scene::Scene;
14use crate::ui_element::UiElement;
15
16
17#[cfg(windows)]
18fn disable_quick_edit() {
19    use winapi::um::consoleapi::SetConsoleMode;
20    use winapi::um::wincon::{ENABLE_EXTENDED_FLAGS, ENABLE_WINDOW_INPUT, ENABLE_MOUSE_INPUT};
21    use winapi::um::winbase::STD_INPUT_HANDLE;
22    use winapi::um::processenv::GetStdHandle;
23    unsafe {
24        let handle = GetStdHandle(STD_INPUT_HANDLE);
25        SetConsoleMode(handle, ENABLE_EXTENDED_FLAGS);
26        SetConsoleMode(handle, ENABLE_WINDOW_INPUT | ENABLE_MOUSE_INPUT);
27    };
28}
29
30#[cfg(not(windows))]
31fn disable_quick_edit() {
32}
33
34pub struct ConsoleUpdateInfo {
35    cursor_pos: (u16, u16),
36    input_events: InputEvents,
37    exit: bool,
38    new_scene: Option<Scene>,
39}
40
41impl ConsoleUpdateInfo {
42    pub fn get_events(&self) -> &InputEvents {
43        &self.input_events
44    }
45    pub fn set_cursor(&mut self, new_cursor_pos: (u16, u16)) {
46        self.cursor_pos = new_cursor_pos;
47    }
48    pub fn request_exit(&mut self) { self.exit = true; }
49    pub fn new_scene(&mut self, scene: Scene) { self.new_scene = Some(scene); }
50}
51
52pub struct Console {
53    buffer: SizedBuffer,
54    scenes: Vec<Scene>,
55    cursor_pos: (u16, u16),
56    should_stop: bool,
57}
58
59impl Console {
60
61    fn full_render_chars(&self) -> Result<()>{
62        stdout().execute(terminal::Clear(ClearType::All)).unwrap();
63
64        for y in 0..self.buffer.height(){
65            for x in 0..self.buffer.width(){
66                print!("{0}", self.buffer.get_pixel(x,y).content);
67            }
68            print!("\n");
69        }
70        Ok(())
71    }
72
73    fn update_render_chars(&self, old_buffer: SizedBuffer) -> Result<()> {
74        let mut stdout = std::io::stdout();
75        for change in self.buffer.compare(&old_buffer) {
76            queue!(stdout, cursor::MoveTo(change.position.0, change.position.1),
77                style::PrintStyledContent(change.value.to_styled_content()))?;
78        }
79        stdout.flush()?;
80        Ok(())
81    }
82
83    fn render(&mut self) {
84        if self.scenes.is_empty() {
85            return;
86        }
87        let old_buffer = self.buffer.clone();
88        self.buffer = SizedBuffer::new(self.buffer.width(), self.buffer.height());
89        self.scenes.last().unwrap().render(&mut self.buffer);
90        self.update_render_chars(old_buffer).unwrap();
91        stdout().execute(cursor::MoveTo(self.cursor_pos.0, self.cursor_pos.1)).unwrap();
92    }
93
94    fn update(&mut self, update_info: &mut ConsoleUpdateInfo) {
95        if self.scenes.is_empty() {
96            return;
97        }
98        self.get_current_scene_mut().unwrap().update(update_info);
99        self.cursor_pos = update_info.cursor_pos;
100    }
101
102    pub fn new() -> Console {
103        let (w, h) = size().expect("Failed to get terminal size.");
104        Console{ buffer: SizedBuffer::new(w, h), scenes: vec![], cursor_pos: (0, 0), should_stop: false }
105    }
106
107    pub fn add_scene(&mut self, scene: Scene){
108        self.scenes.push(scene);
109    }
110
111    pub fn main_loop(&mut self, update_callback: fn(console: &mut Console, update_info: &mut ConsoleUpdateInfo)){
112        disable_quick_edit();
113        let _raw = RawScreen::into_raw_mode();
114        stdout().execute(terminal::Clear(ClearType::All)).unwrap();
115        let input = input::input();
116        let mut reader = input.read_async();
117        loop{
118            let mut update_info = ConsoleUpdateInfo {
119                cursor_pos: self.cursor_pos,
120                input_events: InputEvents::new(&mut reader),
121                exit: false,
122                new_scene: None
123            };
124            update_callback(self, &mut update_info);
125            self.update(&mut update_info);
126            self.render();
127            if self.should_stop || update_info.exit { break; }
128            if let Some(scene) = update_info.new_scene {
129                self.add_scene(scene);
130            }
131            sleep(Duration::from_millis(10));
132        }
133    }
134
135    pub fn get_current_scene_mut(&mut self) -> Option<&mut Scene> {
136        self.scenes.last_mut()
137    }
138
139    pub fn get_current_scene(&self) -> Option<&Scene> {
140        self.scenes.last()
141    }
142
143    pub fn exit(&mut self) {
144        self.should_stop = true;
145    }
146}