console_ui_engine_null/
console.rs1#[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}