#[allow(unused_imports)]
use glutin::VirtualKeyCode;
use std::cell::{Cell, RefCell};
use std::time::{Duration, SystemTime};
use display::Display;
use font::Font;
use text_buffer::TextBuffer;
use renderer;
use input::Input;
static IOSEVKA_SFL: &'static str = include_str!("../fonts/iosevka.sfl");
static IOSEVKA_PNG: &'static [u8] = include_bytes!("../fonts/iosevka.png");
pub struct TerminalBuilder {
title: String,
dimensions: (u32, u32),
clear_color: (f32, f32, f32, f32),
font: Font,
visibility: bool,
headless: bool,
text_buffer_aspect_ratio: bool,
}
#[allow(dead_code)]
impl TerminalBuilder {
pub fn new() -> TerminalBuilder {
TerminalBuilder {
title: "Hello, World ! ".to_owned(),
dimensions: (1280, 720),
clear_color: (0.14, 0.19, 0.28, 1.0),
font: Font::load_raw(IOSEVKA_SFL, IOSEVKA_PNG),
visibility: true,
headless: false,
text_buffer_aspect_ratio: true,
}
}
pub fn with_title<T: Into<String>>(mut self, title: T) -> TerminalBuilder {
self.title = title.into();
self
}
pub fn with_dimensions(mut self, dimensions: (u32, u32)) -> TerminalBuilder {
self.dimensions = dimensions;
self
}
pub fn with_clear_color(mut self, clear_color: (f32, f32, f32, f32)) -> TerminalBuilder {
self.clear_color = clear_color;
self
}
pub fn with_font(mut self, font: Font) -> TerminalBuilder {
self.font = font;
self
}
pub fn with_visibility(mut self, visibility: bool) -> TerminalBuilder {
self.visibility = visibility;
self
}
pub fn with_headless(mut self, headless: bool) -> TerminalBuilder {
self.headless = headless;
self
}
pub fn with_text_buffer_aspect_ratio(mut self, tbar: bool) -> TerminalBuilder {
self.text_buffer_aspect_ratio = tbar;
self
}
pub fn build(self) -> Terminal {
Terminal::new(
self.title,
self.dimensions,
self.clear_color,
self.font,
self.visibility,
self.headless,
self.text_buffer_aspect_ratio,
)
}
}
pub struct Terminal {
display: Option<Display>,
program: renderer::Program,
background_program: renderer::Program,
debug_program: renderer::Program,
debug: Cell<bool>,
running: Cell<bool>,
pub(crate) headless: bool,
since_start: SystemTime,
pub(crate) font: Font,
frame_counter: RefCell<FrameCounter>,
text_buffer_aspect_ratio: bool,
}
impl Terminal {
fn new<T: Into<String>>(
title: T,
window_dimensions: (u32, u32),
clear_color: (f32, f32, f32, f32),
font: Font,
visibility: bool,
headless: bool,
text_buffer_aspect_ratio: bool,
) -> Terminal {
let display;
let program;
let background_program;
let debug_program;
if headless {
display = None;
program = 0;
background_program = 0;
debug_program = 0;
} else {
display = Some(Display::new(
title,
window_dimensions,
clear_color,
visibility,
));
program = renderer::create_program(renderer::VERT_SHADER, renderer::FRAG_SHADER);
background_program =
renderer::create_program(renderer::VERT_SHADER, renderer::BG_FRAG_SHADER);
debug_program =
renderer::create_program(renderer::VERT_SHADER, renderer::DEBUG_FRAG_SHADER);
}
let font = font;
Terminal {
display,
program,
background_program,
debug_program,
debug: Cell::new(false),
running: Cell::new(true),
headless,
since_start: SystemTime::now(),
font,
frame_counter: RefCell::new(FrameCounter::new()),
text_buffer_aspect_ratio,
}
}
pub fn set_debug(&self, debug: bool) {
if !self.headless {
renderer::set_debug(debug);
self.debug.set(debug);
}
}
#[cfg(debug_assertions)]
pub fn refresh(&self) -> bool {
let mut frame_counter = self.frame_counter.borrow_mut();
frame_counter.update();
drop(frame_counter);
if let Some(ref display) = self.display {
let input = self.get_current_input();
if input.was_just_pressed(VirtualKeyCode::F3) {
self.set_debug(!self.debug.get());
}
display.refresh() && self.running.get()
} else {
self.running.get()
}
}
#[cfg(not(debug_assertions))]
pub fn refresh(&self) -> bool {
let mut frame_counter = self.frame_counter.borrow_mut();
frame_counter.update();
drop(frame_counter);
if let Some(ref display) = self.display {
display.refresh() && self.running.get()
} else {
self.running.get()
}
}
pub fn flush(&self, text_buffer: &mut TextBuffer) {
text_buffer.swap_buffers(&self.font);
}
pub fn draw(&self, text_buffer: &TextBuffer) {
if let (&Some(ref display), &Some(ref mesh), &Some(ref background_mesh)) = (
&self.display,
&text_buffer.mesh,
&text_buffer.background_mesh,
) {
if self.text_buffer_aspect_ratio && text_buffer.aspect_ratio != display.get_aspect_ratio() {
display.set_aspect_ratio(text_buffer.aspect_ratio);
}
renderer::clear();
let duration = SystemTime::now().duration_since(self.since_start).unwrap();
let time = duration.as_secs() as f32 + duration.subsec_nanos() as f32 / 1_000_000_000.0;
renderer::draw(
self.get_background_program(),
display.proj_matrix.get(),
time,
background_mesh,
);
renderer::draw(self.get_program(), display.proj_matrix.get(), time, mesh);
}
}
pub fn get_current_input(&self) -> Input {
if let Some(ref display) = self.display {
display.get_current_input()
} else {
Input::new()
}
}
pub fn close(&self) {
self.running.set(false);
}
pub fn set_title<T: Into<String>>(&mut self, title: T) {
if let Some(ref mut display) = self.display {
display.set_title(&title.into());
}
}
pub fn show(&mut self) {
if let Some(ref mut display) = self.display {
display.show();
}
}
pub fn get_fps(&self) -> f32 {
self.frame_counter.borrow().get_fps()
}
pub(crate) fn get_program(&self) -> renderer::Program {
if self.headless {
panic!("Unable to get program from headless terminal");
}
if !self.debug.get() {
self.program
} else {
self.debug_program
}
}
pub(crate) fn get_background_program(&self) -> renderer::Program {
if self.headless {
panic!("Unable to get program from headless terminal");
}
if !self.debug.get() {
self.background_program
} else {
self.debug_program
}
}
#[cfg(test)]
pub(crate) fn update_virtual_keycode(&mut self, keycode: VirtualKeyCode, pressed: bool) {
if let Some(ref mut display) = self.display {
display.update_virtual_keycode(keycode, pressed);
}
}
}
pub(crate) struct FrameCounter {
frames: u32,
last_check: SystemTime,
fps: f32,
}
impl FrameCounter {
pub fn new() -> FrameCounter {
FrameCounter {
frames: 0,
last_check: SystemTime::now(),
fps: 0.0,
}
}
pub fn update(&mut self) {
self.frames += 1;
let current_time = SystemTime::now();
if current_time.duration_since(self.last_check).unwrap() > Duration::from_secs(1) {
self.fps = self.frames as f32;
self.last_check = current_time;
self.frames = 0;
}
}
pub fn get_fps(&self) -> f32 {
self.fps
}
}