summaryrefslogtreecommitdiff
path: root/src/main.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/main.rs')
-rw-r--r--src/main.rs266
1 files changed, 64 insertions, 202 deletions
diff --git a/src/main.rs b/src/main.rs
index 8a6a33b..70e0884 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -7,173 +7,19 @@ use minifb::{Key, KeyRepeat, Window, WindowOptions};
use std::collections::HashMap;
use std::ffi::CString;
-use std::fmt::Write;
use std::vec;
-use ttf_parser::{Face, OutlineBuilder, Rect};
+use ttf_parser::Face;
-use vte::{Params, Parser, Perform};
+use vte::Parser;
-use zeno::{Mask, Transform};
+use zeno::{Fill, Mask, Stroke, Style, Transform};
-const FONT: &[u8] = std::include_bytes!("../fonts/Miracode.ttf");
+mod config;
+use config::*;
-struct Buffer<T: std::clone::Clone> {
- buffer: Vec<T>,
- width: usize,
- height: usize,
-}
-impl<T: Clone> Buffer<T> {
- fn new(val: T, width: usize, height: usize) -> Self {
- Buffer {
- buffer: vec![val; width * height],
- width: width,
- height: height,
- }
- }
- fn get(&self, col: usize, row: usize) -> T {
- self.buffer.get(col + row * self.width).unwrap().clone()
- }
- fn set(&mut self, col: usize, row: usize, val: T) {
- self.buffer[col + row * self.width] = val;
- }
-}
-
-struct Cursor {
- col: usize,
- row: usize,
-}
-
-struct Model {
- screenbuffer: Buffer<u32>,
- buffer: Buffer<Option<char>>,
- cell: Rect,
- cursor: Cursor,
-}
-impl Model {
- // `cell` is the bbox of a cell
- // width and height are measured in cells
- fn new(cell: Rect, width: usize, height: usize, scale: f32) -> Self {
- Model {
- screenbuffer: Buffer::new(
- 0,
- (cell.width() as f32 * width as f32 * scale) as usize,
- (cell.height() as f32 * height as f32 * scale) as usize,
- ),
- buffer: Buffer::new(None, width, height),
- cell: cell,
- cursor: Cursor { col: 0, row: 0 },
- }
- }
-
- // returns scale
- fn scale(&self) -> f32 {
- self.screenbuffer.height as f32 / (self.cell.height() as f32 * self.buffer.height as f32)
- }
-}
-impl Perform for Model {
- // draw a character to the screen and update states
- fn print(&mut self, c: char) {
- if self.cursor.col < self.buffer.width {
- // scrolls
- while self.cursor.row >= self.buffer.height {
- for row in 0..(self.buffer.height - 1) {
- for col in 0..self.buffer.width {
- self.buffer.set(col, row, self.buffer.get(col, row + 1));
- }
- }
- for col in 0..self.buffer.width {
- self.buffer.set(col, self.buffer.height - 1, None);
- }
- self.cursor.row -= 1;
- }
- self.buffer.set(self.cursor.col, self.cursor.row, Some(c));
- self.cursor.col += 1;
- }
- println!("[print] {:?}", c);
- }
-
- // execute a C0 or C1 control function
- fn execute(&mut self, byte: u8) {
- match byte {
- 0x0D => self.cursor.col = 0,
- 0x0A => self.cursor.row = self.cursor.row + 1,
- 0x08 => {
- self.cursor.col = self.cursor.col - 1;
- self.buffer.set(self.cursor.col, self.cursor.row, None);
- }
- _ => (),
- }
- println!("[execute] {:02x}", byte);
- }
-
- // invoked when a final character arrives in first part of device control string
- fn hook(&mut self, params: &Params, intermediates: &[u8], ignore: bool, c: char) {
- println!(
- "[hook] params={:?}, intermediates={:?}, ignore={:?}, char={:?}",
- params, intermediates, ignore, c
- );
- }
-
- // pass bytes as part of a device control string to the handle chosen by hook
- // C0 controls are also passed to this handler
- fn put(&mut self, byte: u8) {
- println!("[put] {:02x}", byte);
- }
-
- // called when a device control string is terminated
- fn unhook(&mut self) {
- println!("[unhook]");
- }
-
- // dispatch an operating system command
- fn osc_dispatch(&mut self, params: &[&[u8]], bell_terminated: bool) {
- println!(
- "[osc_dispatch] params={:?} bell_terminated={}",
- params, bell_terminated
- );
- }
-
- // a final character has arrived for a csi sequence
- fn csi_dispatch(&mut self, params: &Params, intermediates: &[u8], ignore: bool, c: char) {
- println!(
- "[csi_dispatch] params={:#?}, intermediates={:?}, ignore={:?}, char={:?}",
- params, intermediates, ignore, c
- );
- }
-
- // the final character of an escape sequence has arrived
- fn esc_dispatch(&mut self, intermediates: &[u8], ignore: bool, byte: u8) {
- println!(
- "[esc_dispatch] intermediates={:?}, ignore={:?}, byte={:02x}",
- intermediates, ignore, byte
- );
- }
-}
-
-// yoinked from tty_parser docs
-struct Builder(String);
-impl OutlineBuilder for Builder {
- fn move_to(&mut self, x: f32, y: f32) {
- write!(&mut self.0, "M {} {} ", x, y).unwrap();
- }
-
- fn line_to(&mut self, x: f32, y: f32) {
- write!(&mut self.0, "L {} {} ", x, y).unwrap();
- }
-
- fn quad_to(&mut self, x1: f32, y1: f32, x: f32, y: f32) {
- write!(&mut self.0, "Q {} {} {} {} ", x1, y1, x, y).unwrap();
- }
-
- fn curve_to(&mut self, x1: f32, y1: f32, x2: f32, y2: f32, x: f32, y: f32) {
- write!(&mut self.0, "C {} {} {} {} {} {} ", x1, y1, x2, y2, x, y).unwrap();
- }
-
- fn close(&mut self) {
- write!(&mut self.0, "Z ").unwrap();
- }
-}
+mod structs;
+use structs::*;
fn main() {
// initialize the font
@@ -181,84 +27,96 @@ fn main() {
let font = generate_font(&face);
let mut model = Model::new(
- Rect {
- x_min: 0,
- y_min: 0,
- x_max: face.height(),
- y_max: face.height(),
- },
- 80,
- 24,
- 0.008,
+ MODEL_WIDTH,
+ MODEL_HEIGHT,
+ MODEL_SCALE,
);
// initialize window and its buffer
let mut window = Window::new(
- "rustty",
- model.screenbuffer.width,
- model.screenbuffer.height,
+ WINDOW_TITLE,
+ model.screenbuffer_width(),
+ model.screenbuffer_height(),
WindowOptions::default(),
)
.unwrap();
- window.set_target_fps(60);
+ window.set_target_fps(WINDOW_TARGET_FPS);
+ // pty and pid of child
let (pty, child) = spawn_pty().unwrap();
+
+ // set pty as non-blocking
fcntl(
&pty,
F_SETFL(OFlag::from_bits_truncate(fcntl(&pty, F_GETFL).unwrap()) | OFlag::O_NONBLOCK),
)
.unwrap();
+ // parser for `vte`
let mut statemachine = Parser::new();
- let mut buf = [0u8; 2048];
+ // buffers for input and writing to the screen
+ let mut buf = [0u8; INPUT_BUFFER_SIZE];
+ let mut mask = vec![0u8; model.screenbuffer_width() * model.screenbuffer_height()];
+
+ // window is open, child is alive
while window.is_open()
- && !window.is_key_down(Key::Escape)
&& waitpid(child, Some(WaitPidFlag::WNOHANG)) == Ok(WaitStatus::StillAlive)
{
- // mask to render into
- let mut mask = vec![0u8; model.screenbuffer.width * model.screenbuffer.height];
+ // clear mask from last frame
+ mask.fill(0u8);
// render each char into mask
- for row in 0..model.buffer.height {
- for col in 0..model.buffer.width {
- if let Some(c) = model.buffer.get(col, row) {
- Mask::new(font.get(&c).map_or("", |v| v)) // get svg
+ // remember that `model.buffer` is always 1-indexed
+ for row in 1..=model.buffer_height() {
+ for col in 1..=model.buffer_width() {
+ if let Some(chr) = model.buffer_at(row, col).chr() {
+ Mask::new(font.get(&chr).map_or("", |v| v)) // get svg
.transform(Some(
Transform::scale(model.scale(), -model.scale()) // scale it
.then_translate(
- col as f32 * model.cell.width() as f32 * model.scale(),
- // shift right by the cell width * the scale
- (1 + row) as f32 * model.scale() * model.cell.height() as f32,
+ // col and row are 1 indexed; we want col to be 0 indexed bc
+ // this refers to the bottom left corner of each cell, or at
+ // least behaves like it
+ (col as f32 - 1.0) * model.cell_width() as f32 * model.scale(),
+ row as f32 * model.scale() * model.cell_height() as f32,
),
))
+ .style(
+ match model.buffer_at(row, col).char_attr() {
+ CharAttr::Normal => Style::Fill(Fill::NonZero),
+ CharAttr::Bold => Style::Stroke(Stroke::new(500.0)),
+ CharAttr::Inverse => Style::Stroke(Stroke::new(50.0)),
+ _ => Style::Stroke(Stroke::new(50.0)),
+ }
+ )
.size(
- model.screenbuffer.width as u32,
- model.screenbuffer.height as u32,
+ // fits transformed svg to screen, or trims excess
+ model.screenbuffer_width() as u32,
+ model.screenbuffer_height() as u32,
)
+ // writes it to mask
.render_into(&mut mask, None);
}
}
}
- // render in white/grayscale to screen
- for (p, m) in model.screenbuffer.buffer.iter_mut().zip(mask.iter()) {
- let m0 = *m as u32;
- *p = m0 << 16 | m0 << 8 | m0;
- }
+ // render mask onto screen
+ model.set_screenbuffer(&mask);
- // update screen with buffer
+ // update screen with new screenbuffer
window
.update_with_buffer(
- &model.screenbuffer.buffer,
- model.screenbuffer.width,
- model.screenbuffer.height,
+ model.screenbuffer_buffer(),
+ model.screenbuffer_width(),
+ model.screenbuffer_height(),
)
.unwrap();
// other stuff
match read(&pty, &mut buf) {
Ok(0) => (),
+ // if there are new chars, feed them to `vte`
Ok(n) => statemachine.advance(&mut model, &buf[..n]),
Err(_e) => (),
};
@@ -268,10 +126,10 @@ fn main() {
|| window.is_key_down(Key::RightShift)
|| window.is_key_down(Key::CapsLock);
let ctrl = window.is_key_down(Key::LeftCtrl) || window.is_key_down(Key::RightCtrl);
+ // writes keystrokes to buffer, will be processed by vte next frame
if !keys.is_empty() {
let bytes: Vec<u8> = keys
.iter()
- // TODO apply modifiers
.map(|key| {
key_to_u8(
*key, // key
@@ -299,12 +157,16 @@ fn spawn_pty() -> Option<(PtyMaster, Pid)> {
&CString::new("/usr/bin/env").unwrap(),
&[
CString::new("/usr/bin/env").unwrap(),
- CString::new("-i").unwrap(),
- CString::new("sh").unwrap(),
+ CString::new("bash").unwrap(),
CString::new("--norc").unwrap(),
CString::new("--noprofile").unwrap(),
],
- &[CString::new("TERM=dumb").unwrap()],
+ &[
+ CString::new("TERM=".to_string() + ENV_TERM).unwrap(),
+ CString::new("PATH=".to_string() + ENV_PATH).unwrap(),
+ CString::new("NIXPKGS_CONFIG=/etc/nix/nixpkgs-config.nix").unwrap(),
+ CString::new("NIX_PATH=nixpkgs=flake:nixpkgs:/nix/var/nix/profiles/per-user/root/channels").unwrap(),
+ ],
);
return None;
}
@@ -313,7 +175,7 @@ fn spawn_pty() -> Option<(PtyMaster, Pid)> {
};
}
-// WARNING not functional; missing some keys and also modifiers
+// returns unrecognised keys as null bytes
fn key_to_u8(key: Key, shift: bool, ctrl: bool) -> u8 {
let mut base = match key {
Key::Key0 => b'0',
@@ -475,7 +337,7 @@ fn generate_font(face: &Face) -> HashMap<char, String> {
let mut hm = HashMap::new();
for c in chars {
- let mut builder = Builder(String::new());
+ let mut builder = Builder::new();
face.outline_glyph(face.glyph_index(c).unwrap(), &mut builder)
.unwrap();
hm.entry(c).insert_entry(builder.0);