1extern crate libc;
29
30use crate::math::Vec2;
31use crate::img::{BlendMode, Color, Image};
32use crate::input::Input;
33use crate::screen_buffer::*;
34
35use termios::*;
36
37use std::mem;
38
39use std::io::{stdout, Write};
40
41use std::panic;
42use std::backtrace::Backtrace;
43
44use std::thread;
45use std::sync::{mpsc, Barrier, Arc, Mutex};
46
47use std::io::stdin;
48use std::os::unix::io::AsRawFd;
49
50const NCCS: usize = 32;
51
52
53macro_rules! csi {
55 ($( $l:expr ),*) => { concat!("\x1b[", $( $l ),*) };
56}
57
58
59enum RenderingDirective {
61 DrawLine(Vec2, Vec2, Color),
62 DrawRect(Vec2, Vec2, Color),
63 DrawRectBoudary(Vec2, Vec2, Color),
64 DrawEllipseBoudary(Vec2, Vec2, Color),
65 DrawPoint(Vec2, Color),
66
67 DrawImage(Arc<Mutex<Image>>, Vec2, Vec2, Vec2, Option<Color>),
68 DrawWholeImageAlpha(Arc<Mutex<Image>>, Vec2, Color),
69 DrawWholeImage(Arc<Mutex<Image>>, Vec2),
70
71 PrintTextRaw(String, Vec2, CharBackgroundMode, CharForegroundMode),
72
73 ClearColor(Color),
74 ClearText,
75
76 UpdateScreenSize(Vec2),
77 BeginFrame,
78 PushFrame
79}
80
81
82pub struct Renderer {
106 termios: Termios,
107 default_c_lflags: u32,
108 default_c_cc: [u8; NCCS],
109
110 building_frame: bool,
111 prev_screen_size: Vec2,
112
113 _server_handle: Option<thread::JoinHandle<()>>,
114 sender: mpsc::Sender<RenderingDirective>,
115
116 frame_barrier: Arc<Barrier>
117}
118
119
120static mut RENDERER: Option<Renderer> = None;
122
123
124impl Renderer {
125
126 fn init() -> Renderer {
128 let stdinfd = stdin().as_raw_fd();
129
130 let mut termios = match Termios::from_fd(stdinfd) {
131 Ok(t) => t,
132 Err(_) => panic!("Could not read stdin fd")
133 };
134
135 let default_c_lflags = termios.c_lflag;
137 let default_c_cc = termios.c_cc;
138
139 termios.c_lflag &= !(ECHO | ICANON | ISIG);
140 termios.c_cc[VMIN] = 1;
141 termios.c_cc[VTIME] = 0;
142
143 tcsetattr(stdinfd, TCSANOW, &mut termios).expect("could not set stdin attributes");
144
145 print!("{}{}",
146 csi!("?25l"), csi!("?1049h") );
149 stdout().flush().expect("Could not write to stdout");
150
151 panic::set_hook(Box::new(|panic_info| {
153 let backtrace = Backtrace::capture();
154 eprintln!("{}", backtrace);
155 eprintln!("{}", panic_info);
156 Renderer::exit();
157 }));
158
159 let (rx, tx) = mpsc::channel();
161 let barrier = Arc::new(Barrier::new(2));
162 let frame_barrier = Arc::clone(&barrier);
163
164 let handle = thread::spawn(move || {
165 let mut screen_size = Renderer::get_size();
166 let mut screen : ScreeBuffer = ScreeBuffer::new((0, 0));
167 let mut prev_screen: ScreeBuffer = ScreeBuffer::new((0, 0));
168
169 let mut back: Color = Color::BLACK;
170 let mut fore: Color = Color::BLACK;
171 print!("{:-}{:+}", back, fore);
172
173
174 loop {
175 match tx.recv().expect("RenderingServer channel was destroyed") {
176 RenderingDirective::DrawLine(p1, p2, c) => screen.line(p1, p2, c),
177 RenderingDirective::DrawRect(p, s, c) => screen.rect(p, s, c),
178 RenderingDirective::DrawRectBoudary(p, s, c) => screen.rect_boudary(p, s, c),
179 RenderingDirective::DrawEllipseBoudary(center, s, c) => screen.ellipse_boundary(center, s, c),
180 RenderingDirective::DrawPoint(p, c) => screen.point(p, c),
181
182 RenderingDirective::DrawImage(img, pos, size, off, alpha) => screen.image(&(*img.lock().unwrap()), pos, size, off, alpha),
183 RenderingDirective::DrawWholeImageAlpha(img, pos, alpha) => screen.whole_image_alpha(&(*img.lock().unwrap()), pos, alpha),
184 RenderingDirective::DrawWholeImage(img, pos) => screen.whole_image(&(*img.lock().unwrap()), pos),
185
186 RenderingDirective::PrintTextRaw(text, pos, back_mode, fore_mode) => screen.print_text_raw(text, pos, back_mode, fore_mode),
187
188 RenderingDirective::ClearColor(c) => screen.clear_color(c),
189 RenderingDirective::ClearText => screen.clear_text(),
190
191 RenderingDirective::UpdateScreenSize(size) => {
192 screen_size = size;
193 screen.raw_resize(size); }
195
196 RenderingDirective::BeginFrame => {frame_barrier.wait(); ()},
197 RenderingDirective::PushFrame => {
198 print!("\x1b[H");
200
201 let mut skiped = false;
202
203 for j in (0..screen_size.y).step_by(2) {
204 for i in 0..screen_size.x {
205 let pos1 = vec2!(i, j);
206 let pos2 = vec2!(i, j + 1);
207
208 let color1 = screen.get_color(pos1);
209 let color2 = screen.get_color(pos2);
210 let char_data = screen.get_char_data(pos1);
211
212 let prev_color1 = prev_screen.get_color(pos1);
213 let prev_color2 = prev_screen.get_color(pos2);
214 let prev_char_data = prev_screen.get_char_data(pos1);
215
216 if screen.size() == prev_screen.size()
218 && char_data == prev_char_data
219 && color1 == prev_color1
220 && color2 == prev_color2 {
221 skiped = true;
222 continue;
223 }
224
225 if skiped {
227 print!("\x1b[{};{}H", j/2 + 1, i + 1);
228 skiped = false;
229 }
230
231
232 match char_data.c {
233 Some(c) => {
234 let background_color = match char_data.bg_mode {
236 CharBackgroundMode::Blend => Color::blend(color1, color2, BlendMode::Add),
237 CharBackgroundMode::Colored(c) => c
238 };
239 let foreground_color = match char_data.fg_mode {
240 CharForegroundMode::Opposite => {
241 let (_, _, l) = background_color.get_okhsl();
244 if l > 0.5 {
245 Color::BLACK
246 } else {
247 Color::WHITE
248 }
249 },
250 CharForegroundMode::Colored(c) => c
251 };
252
253 if back != background_color {
255 back = background_color;
256 print!("{:-}", back);
257 }
258 if fore != foreground_color {
259 fore = foreground_color;
260 print!("{:+}", fore);
261 }
262
263 print!("{}", c);
265 }
266 None => {
267
268
269 if color1 != back && color1 != fore && color2 == back {
271 fore = color1;
272 print!("{:+}", fore);
273 } else if color1 != back && color1 != fore && color2 == fore {
274 back = color1;
275 print!("{:-}", back);
276 } else if color2 != back && color2 != fore && color1 == back {
277 fore = color2;
278 print!("{:+}", fore);
279 } else if color2 != back && color2 != fore && color1 == fore {
280 back = color2;
281 print!("{:-}", back);
282 } else if color1 != back && color1 != fore && color2 != back && color2 != fore {
283 fore = color1;
284 back = color2;
285 print!("{:+}", fore);
286 print!("{:-}", back);
287 }
288
289 if color1 == back && color2 == back {
291 print!(" ");
292 } else if color1 == back && color2 == fore {
293 print!("▄");
294 } else if color1 == fore && color2 == back {
295 print!("▀");
296 } else if color1 == fore && color2 == fore {
297 print!("█");
298 }
299 }
300 }
301 }
302 }
303 stdout().flush().expect("Could not write to stdout");
304 prev_screen = screen.clone();
305 }
306 }
307 }
308 });
309
310 Renderer {
311 termios: termios,
312 default_c_lflags: default_c_lflags,
313 default_c_cc: default_c_cc,
314
315 building_frame: false,
316 prev_screen_size: Vec2::ZERO,
317
318 _server_handle: Some(handle),
319 sender: rx,
320
321 frame_barrier: barrier
322 }
323 }
324
325
326 pub fn exit() {
328 unsafe {
329 RENDERER = None;
330 }
331 }
332
333
334 pub fn get() -> &'static mut Renderer {
336 unsafe {
337 match &mut RENDERER {
338 None => { RENDERER = Some(Renderer::init());
340 Renderer::get()
341 }
342 Some(r) => r
343 }
344 }
345 }
346
347
348 pub fn get_size() -> Vec2 {
356 unsafe {
357 let mut size: TermSize = mem::zeroed();
358 libc::ioctl(libc::STDOUT_FILENO, libc::TIOCGWINSZ, &mut size as *mut _);
359 vec2!(size.col as i32, 2 * size.row as i32)
360 }
361 }
362
363
364 fn can_draw(&self) {
366 if !self.building_frame { panic!("drawing outside of a frame build (call begin_draw)"); }
367 }
368
369
370 pub fn begin_draw(&mut self) {
374 if self.building_frame {
375 panic!("begin_draw called when already building a frame");
376 }
377 self.building_frame = true;
378 let new_size = Renderer::get_size();
379 if self.prev_screen_size != new_size {
380 self.sender.send(RenderingDirective::UpdateScreenSize(new_size)).expect("Rendering thread stopped");
381 self.prev_screen_size = new_size;
382 }
383
384 self.sender.send(RenderingDirective::BeginFrame).expect("Rendering thread stopped");
385 self.frame_barrier.wait();
386 }
387
388
389 pub fn end_draw(&mut self) {
391 if !self.building_frame {
392 panic!("end_draw called when already building a frame");
393 }
394 self.building_frame = false;
395 self.sender.send(RenderingDirective::PushFrame).expect("Rendering thread stopped");
396 }
397
398
399 pub fn clear(&mut self, c: Color) {
401 self.can_draw();
402 self.sender.send(RenderingDirective::ClearColor(c)).expect("Rendering thread stopped");
403 self.sender.send(RenderingDirective::ClearText).expect("Rendering thread stopped");
404 }
405
406
407 pub fn clear_color(&mut self, c: Color) {
409 self.can_draw();
410 self.sender.send(RenderingDirective::ClearColor(c)).expect("Rendering thread stopped");
411 }
412
413
414 pub fn clear_text(&mut self) {
416 self.can_draw();
417 self.sender.send(RenderingDirective::ClearText).expect("Rendering thread stopped");
418 }
419
420
421 pub fn draw_line<A, B>(&mut self, p1: A, p2: B, c: Color)
423 where A: AsRef<Vec2>, B: AsRef<Vec2>
424 {
425 self.can_draw();
426 self.sender.send(RenderingDirective::DrawLine(*p1.as_ref(), *p2.as_ref(), c))
427 .expect("Rendering thread stopped");
428 }
429
430
431 pub fn draw_rect<A, B>(&mut self, p: A, s: B, c: Color)
434 where A: AsRef<Vec2>, B: AsRef<Vec2>
435 {
436 self.can_draw();
437 self.sender.send(RenderingDirective::DrawRect(*p.as_ref(), *s.as_ref(), c))
438 .expect("Rendering thread stopped");
439 }
440
441
442 pub fn draw_rect_boundary<A, B>(&mut self, p: A, s: B, c: Color)
444 where A: AsRef<Vec2>, B: AsRef<Vec2>
445 {
446 self.can_draw();
447 self.sender.send(RenderingDirective::DrawRectBoudary(*p.as_ref(), *s.as_ref(), c))
448 .expect("Rendering thread stopped");
449 }
450
451
452 pub fn draw_ellipse_boundary<A, B>(&mut self, c: A, s: B, col: Color)
455 where A: AsRef<Vec2>, B: AsRef<Vec2>
456 {
457 self.can_draw();
458 self.sender.send(RenderingDirective::DrawEllipseBoudary(*c.as_ref(), *s.as_ref(), col))
459 .expect("Rendering thread stopped");
460 }
461
462
463 pub fn draw_point<A>(&mut self, p: A, c: Color)
465 where A: AsRef<Vec2>
466 {
467 self.can_draw();
468 self.sender.send(RenderingDirective::DrawPoint(*p.as_ref(), c)).expect("Rendering thread stopped");
469 }
470
471
472 pub fn draw_image<A, B, C>(&mut self,
476 img: Arc<Mutex<Image>>, pos: A, size: B, offset: C, alpha: Option<Color>)
477 where A: AsRef<Vec2>, B: AsRef<Vec2>, C: AsRef<Vec2>
478 {
479 self.can_draw();
480 self.sender.send(RenderingDirective::DrawImage(img, *pos.as_ref(), *size.as_ref(), *offset.as_ref(), alpha))
481 .expect("Rendering thread stopped");
482 }
483
484
485 pub fn draw_whole_image_alpha<A>(&mut self, img: Arc<Mutex<Image>>, pos: A, alpha: Color)
492 where A: AsRef<Vec2>
493 {
494 self.can_draw();
495 self.sender.send(RenderingDirective::DrawWholeImageAlpha(img, *pos.as_ref(), alpha))
496 .expect("Rendering thread stopped");
497 }
498
499
500 pub fn draw_whole_image<A>(&mut self, img: Arc<Mutex<Image>>, pos: A)
507 where A: AsRef<Vec2>
508 {
509 self.can_draw();
510 self.sender.send(RenderingDirective::DrawWholeImage(img, *pos.as_ref())).expect("Rendering thread stopped");
511 }
512
513
514 pub fn print_text_raw<A>(&mut self, text: &String, pos: A, background_mode: CharBackgroundMode, foreground_mode: CharForegroundMode)
515 where A: AsRef<Vec2>
516 {
517 self.can_draw();
518 self.sender.send(RenderingDirective::PrintTextRaw(text.clone(), *pos.as_ref(), background_mode, foreground_mode))
519 .expect("Rendering thread stopped");
520 }
521
522
523 pub fn print_blended_text_raw<A>(&mut self, text: &String, pos: A)
524 where A: AsRef<Vec2>
525 {
526 self.print_text_raw(
527 text,
528 pos,
529 CharBackgroundMode::Blend,
530 CharForegroundMode::Opposite
531 );
532 }
533
534
535 pub fn print_colored_text_raw<A>(&mut self, text: &String, pos: A, background_color: Color, foreground_color: Color)
536 where A: AsRef<Vec2>
537 {
538 self.print_text_raw(
539 text,
540 pos,
541 CharBackgroundMode::Colored(background_color),
542 CharForegroundMode::Colored(foreground_color)
543 )
544 }
545
546
547
548 pub fn ring_bell(&self) {
552 self.can_draw();
553 print!("\x07");
554 }
555}
556
557
558impl Drop for Renderer {
559
560 fn drop(&mut self) {
562 self.termios.c_cc = self.default_c_cc;
564 self.termios.c_lflag = self.default_c_lflags;
565
566 let stdinfd = stdin().as_raw_fd();
567 tcsetattr(stdinfd, TCSANOW, &mut self.termios).expect("could not set stdin attributes");
568
569 print!("{}{}",
570 csi!("?25h"), csi!("?1049l") );
573 stdout().flush().expect("Could not write to stdout");
574 Input::disable_mouse();
575
576 std::process::exit(0);
577 }
578}
579
580
581struct TermSize {
582 row: libc::c_ushort,
583 col: libc::c_ushort,
584 _x : libc::c_ushort,
585 _y : libc::c_ushort
586}