#[allow(unused_imports)]
use glutin::VirtualKeyCode;
use std::cell::{Cell, RefCell};
use std::time::SystemTime;
use crate::display::Display;
use crate::events::Events;
use crate::font::Font;
use crate::renderer;
use crate::renderer::Program;
use crate::text_buffer::TextBuffer;
static IOSEVKA_SFL: &'static str = include_str!("../fonts/iosevka.sfl");
static IOSEVKA_PNG: &'static [u8] = include_bytes!("../fonts/iosevka.png");
pub struct TerminalBuilder {
pub(crate) title: String,
pub(crate) dimensions: (u32, u32),
pub(crate) clear_color: (f32, f32, f32, f32),
pub(crate) font: Font,
pub(crate) visibility: bool,
pub(crate) headless: bool,
pub(crate) text_buffer_aspect_ratio: bool,
pub(crate) vsync: 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,
vsync: 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 with_vsync(mut self, vsync: bool) -> TerminalBuilder {
self.vsync = vsync;
self
}
pub fn build(self) -> Terminal {
Terminal::new(self)
}
}
pub struct Terminal {
display: Option<Display>,
program: Program,
background_program: Program,
debug_program: Program,
debug: Cell<bool>,
running: Cell<bool>,
pub(crate) headless: bool,
since_start: SystemTime,
pub(crate) font: Font,
timer: RefCell<Timer>,
text_buffer_aspect_ratio: bool,
}
impl Terminal {
fn new(builder: TerminalBuilder) -> Terminal {
let (display, program, background_program, debug_program) = if builder.headless {
(None, Program::empty(), Program::empty(), Program::empty())
} else {
(
Some(Display::new(
builder.title,
builder.dimensions,
builder.clear_color,
builder.visibility,
builder.text_buffer_aspect_ratio,
builder.vsync,
)),
renderer::create_program(renderer::VERT_SHADER, renderer::FRAG_SHADER),
renderer::create_program(renderer::VERT_SHADER, renderer::BG_FRAG_SHADER),
renderer::create_program(renderer::VERT_SHADER, renderer::DEBUG_FRAG_SHADER),
)
};
Terminal {
display,
program,
background_program,
debug_program,
debug: Cell::new(false),
running: Cell::new(true),
headless: builder.headless,
since_start: SystemTime::now(),
font: builder.font,
timer: RefCell::new(Timer::new()),
text_buffer_aspect_ratio: builder.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 timer = self.timer.borrow_mut();
timer.update();
drop(timer);
let running = if let Some(ref display) = self.display {
let events = self.get_current_events();
if events.keyboard.was_just_pressed(VirtualKeyCode::F3) {
self.set_debug(!self.debug.get());
}
display.refresh() && self.running.get()
} else {
self.running.get()
};
if running && !self.headless {
renderer::clear();
}
running
}
#[cfg(not(debug_assertions))]
pub fn refresh(&self) -> bool {
let mut timer = self.timer.borrow_mut();
timer.update();
drop(timer);
let running = if let Some(ref display) = self.display {
display.refresh() && self.running.get()
} else {
self.running.get()
};
if running && !self.headless {
renderer::clear();
}
running
}
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,
) {
let proj_matrix = if self.text_buffer_aspect_ratio {
display.get_display_data(&text_buffer).proj_matrix
} else {
display.proj_matrix.get()
};
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(),
proj_matrix,
time,
background_mesh,
);
renderer::draw(self.get_program(), proj_matrix, time, mesh);
}
}
pub fn get_current_events(&self) -> Events {
if let Some(ref display) = self.display {
display.get_current_events()
} else {
Events::new(self.text_buffer_aspect_ratio)
}
}
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 delta_time(&self) -> f32 {
self.timer.borrow().get_delta_time()
}
pub(crate) fn get_program(&self) -> 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) -> 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 Timer {
last_check: SystemTime,
delta_time: f32,
}
impl Timer {
pub fn new() -> Timer {
Timer {
last_check: SystemTime::now(),
delta_time: 0.0,
}
}
pub fn update(&mut self) {
let current_time = SystemTime::now();
let duration = current_time.duration_since(self.last_check).unwrap();
self.last_check = current_time;
self.delta_time =
duration.as_secs() as f32 + duration.subsec_nanos() as f32 / 1_000_000_000.0;
}
pub fn get_delta_time(&self) -> f32 {
self.delta_time
}
}