summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndy Wortman <me@iximeow.net>2017-07-09 03:25:57 -0700
committerAndy Wortman <me@iximeow.net>2017-07-09 03:25:57 -0700
commit4fe34429eeeb6d06ba68d084da37474f61a6ff63 (patch)
tree476947c4b947ffa56f3ae226f76619eb3a30e0bb
parent3b3028f90e99ae4ec573fe6ff8d1755fe5718e91 (diff)
rewrite stuff to handle arrow key input, wip buffering/windowing for render
-rw-r--r--Cargo.toml2
-rw-r--r--main.rs262
2 files changed, 204 insertions, 60 deletions
diff --git a/Cargo.toml b/Cargo.toml
index 673a8a6..ea084a8 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -16,5 +16,5 @@ test = false
bench = false
[dependencies]
-"terminal_size" = "0.1.7"
"termios" = "0.2.2"
+"termion" = "1.4.0"
diff --git a/main.rs b/main.rs
index fa22374..1b935c8 100644
--- a/main.rs
+++ b/main.rs
@@ -1,28 +1,34 @@
+use std::cmp::{min, max};
use std::io;
use std::io::{Seek, SeekFrom};
use std::fs;
use std::fmt::Write;
use std::io::Read;
use std::env;
-use std::path::Path;
+//use std::path::Path;
extern crate termios;
use termios::{Termios, TCSANOW, ECHO, ICANON, tcsetattr};
-extern crate terminal_size;
-use terminal_size::{Width, Height, terminal_size};
+extern crate termion;
+use termion::{color, input};
+use termion::input::TermRead;
+use termion::event::{Event, Key};
fn main() {
println!("hello");
- if let Some((Width(w), Height(h))) = terminal_size() {
- if let Some(arg1) = env::args().nth(1) {
- println!("You would like to work with {}.", arg1);
- launch_interface(w, h, &arg1);
- } else {
- usage();
+ match termion::terminal_size() {
+ Ok((w, h)) => {
+ if let Some(arg1) = env::args().nth(1) {
+ println!("You would like to work with {}.", arg1);
+ launch_interface(w, h, arg1.clone());
+ } else {
+ usage();
+ }
+ }
+ Err(e) => {
+ println!("{}", e);
}
- } else {
- println!("Wasn't able to get terminal dimensions for some reason");
}
}
@@ -30,80 +36,215 @@ fn usage() {
println!("usage: this_thing filename");
}
-fn launch_interface(w: u16, h: u16, filename: &String) {
- println!("Launching interface with width {} and height {}...", w, h);
+struct Program {
+ filename: String,
+ filelen: u64,
+ width: u16,
+ row_size: u16,
+ height: u16,
+ seek: u64,
+ cursor: u64,
+ old_term: Termios,
+ new_term: Termios,
+ buf: Vec<u8>
+}
+
+impl Program {
+ fn inc_cursor(&mut self, amount: u64) {
+ if self.filelen - self.cursor < amount {
+ self.cursor = self.filelen - 1;
+ } else {
+ self.cursor += amount;
+ }
+ self.recalculate_seek();
+ }
+ fn dec_cursor(&mut self, amount: u64) {
+ if self.cursor < amount {
+ self.cursor = 0;
+ } else {
+ self.cursor -= amount;
+ }
+ self.recalculate_seek();
+ }
+ fn recalculate_seek(&mut self) {
+ let old_seek = self.seek;
+ let bufsize = self.height as u64 * self.width as u64;
+ let render_lim = (self.row_size as u64) * (self.height as u64 - 1); // this shouldn't be decided here...
+ if self.cursor + render_lim > self.seek + bufsize { // know a priori that buf size is height * width. TODO: fix.
+ println!("aaaa");
+ // we've sought past the end, so reset seek to what would have been the start of this
+ // display and it'll all work out from here.
+ self.seek = self.cursor - (self.cursor % self.row_size as u64);
+ } else if self.cursor < self.seek {
+ println!("bbbb");
+ // we've sought before the start, so reset seek to just before. ideally, populate a
+ // buffer for like 1k before, but lazy.
+ self.seek = self.cursor - (self.cursor % self.row_size as u64);
+ }
+
+ // repopulate buf..
+ if self.seek != old_seek {
+ self.buf = populate_buf(&self.filename, self.seek, bufsize);
+ }
+ }
+}
- // 4 = columns per group, 3 = xx xx xx xx , ...
- // + (x + 7) / 8 is for ascii at the end of line, rounded up to 8 characters
- // width = 8-20 + (3 * 4) * x + (x + 7) / 8
- // 8 * width = 87 + 57x
- // x = (8 * width - 87) / 57
- let hex_cols = (w * 8 - 87) / 57;
- println!("0x________: {} {}", hex_cols, hex_cols);
+fn launch_interface(w: u16, h: u16, filename: String) {
match fs::metadata(&filename) {
Ok(fs_meta) => {
println!("{} is {} bytes, and is it readable? {}", filename, fs_meta.len(), true);
let termios = Termios::from_fd(0).unwrap();
let mut new_termios = termios.clone();
+
// fix terminal to not echo, thanks
new_termios.c_lflag &= !(ICANON | ECHO);
- tcsetattr(0, TCSANOW, &mut new_termios).unwrap();
- interface_loop(w, h, filename);
+ let initial_buf = populate_buf(&filename, 0, w as u64 * h as u64);
+
+ let mut state = Program {
+ filename: filename,
+ filelen: fs_meta.len(),
+ width: w,
+ // let col_width = 4;
+ // w = 10 + 1 + 1 + 4 * 3 * x + x + 1 + 4 * x
+ // w = 13 + 17 * x
+ // w - 13 / 17 = x
+ row_size: ((w - 13) / 17) * 4,
+ height: h,
+ old_term: termios,
+ new_term: new_termios,
+ cursor: 0,
+ seek: 0,
+ buf: initial_buf
+ };
+
+ tcsetattr(0, TCSANOW, &state.new_term).unwrap();
+
+ interface_loop(&mut state);
- tcsetattr(0, TCSANOW, &termios).unwrap();
+ tcsetattr(0, TCSANOW, &state.old_term).unwrap();
},
Err(e) =>
- println!("{} does not exist.", filename),
+ println!("{} does not exist. ({})", filename, e),
}
}
-fn interface_loop(w: u16, h: u16, filename: &String) {
- let mut cursor: u64 = 0;
- let mut buffer = [0; 0x200];
- populate_buf(filename, cursor, &mut buffer);
- render_interface(w, h, cursor, &buffer);
+fn interface_loop(state: &mut Program) {
+// let buffer = populate_buf(&state.filename, state.cursor, state.width as u64 * state.height as u64); // sw*h over-estimates by about a factor of.. 4?
+ render_interface(state);
let stdin = io::stdin();
- for byte in stdin.lock().bytes() {
- match byte {
- Ok(value) => {
- render_interface(w, h, cursor, &buffer);
- },
- Err(e) => {
- println!("Err: {}", e);
+// for input in KeyboardInput::from_stdin(&stdin.lock().bytes()) {
+ for input in stdin.events() {
+ match input.unwrap() {
+ Event::Key(Key::Up) => {
+ let amount = state.row_size; // why can't this be inlined?
+ state.dec_cursor(amount as u64);
+ }
+ Event::Key(Key::Down) => {
+ let amount = state.row_size;
+ state.inc_cursor(amount as u64);
+ }
+ Event::Key(Key::Left) => {
+ state.dec_cursor(1);
+ }
+ Event::Key(Key::Right) => {
+ state.inc_cursor(1);
+ }
+ Event::Key(Key::PageDown) => {
+ let amount = (state.row_size as u64) * (state.height as u64 - 1);
+ state.inc_cursor(amount);
+ }
+ Event::Key(Key::PageUp) => {
+ let amount = (state.row_size as u64) * (state.height as u64 - 1);
+ state.dec_cursor(amount);
+ }
+ u => {
+ println!("Got {:?}", u);
+ break;
}
}
+ render_interface(state);
+ }
+}
+
+/*
+struct KeyboardInput {
+
+}
+
+impl KeyboardInput {
+ fn from_stdin(x: &Iterator<Item = io::Result<u8>>) -> KeyboardInput {
+ let x = KeyboardInput { };
+ x
+ }
+}
+
+impl Iterator for KeyboardInput {
+ type Item = KeyInfo;
+ fn next(&mut self) -> Option<Self::Item> {
+ Some(0)
}
}
+*/
fn ascii_or_dot(c: u8) -> u8 {
let u = c as char;
- if (u >= 'a' && u <= 'z' || u >= '0' && u <= '9' || u >= 'A' && u <= 'Z' || u == '~' || u == '!' || u == '@' || u == '#' || u == '$' || u == '%' || u == '^' || u == '&' || u == '*' || u == '(' || u == ')' || u == '_' || u == '+' || u == '{' || u == '}' || u == '|' || u == ':' || u == '"' || u == '>' || u == '?' || u == '-' || u == '=' || u == '[' || u == ']' || u == '\\' || u == ';' || u == '\'' || u == '.' || u == '/' || u == '<' || u == ',' || u == '`') { c } else { '.' as u8 }
+ if
+ u >= 'a' && u <= 'z' ||
+ u >= '0' && u <= '9' ||
+ u >= 'A' && u <= 'Z' ||
+ u == '~' || u == '!' ||
+ u == '@' || u == '#' ||
+ u == '$' || u == '%' ||
+ u == '^' || u == '&' ||
+ u == '*' || u == '(' ||
+ u == ')' || u == '_' ||
+ u == '+' || u == '{' ||
+ u == '}' || u == '|' ||
+ u == ':' || u == '"' ||
+ u == '>' || u == '?' ||
+ u == '-' || u == '=' ||
+ u == '[' || u == ']' ||
+ u == '\\' || u == ';' ||
+ u == '\'' || u == '.' ||
+ u == '/' || u == '<' ||
+ u == ',' || u == '`' {
+ c
+ } else {
+ '.' as u8
+ }
}
-fn render_interface(w: u16, h: u16, cursor: u64, buffer: &[u8]) {
- println!("Dimensions: {}x{} Cursor: {} cursor_pos: ({}, {})", w, h, cursor, 0, 0);
- let addr_width = 8;
- // w = 10 + 1 + 1 + 4 * 3 * x + x + 1 + 4 * x
- // w = 13 + 17 * x
- // w - 13 / 17 = x
- let cols = (w - 13) / 17;
- let col_width = 4;
+fn render_interface(state: &Program) {
+ println!("Dimensions: {}x{} Seek: {}, Cursor: {} - file: {}, {} bytes", state.width, state.height, state.seek, state.cursor, state.filename, state.filelen);
+ //let addr_width = 8;
// let ascii = bytes_width * cols;
- let mut idx: usize = 0;
- for i in 1..(h-1) {
+ let buffer = &state.buf;
+
+ let start = (state.cursor - state.seek) as usize;
+ let mut idx: usize = start;
+ let col_width = 4;
+ for i in 1..(state.height-1) {
let mut hex_line = String::new();
let mut ascii_line = String::new();
- for col in 0..cols {
- for c in 0..col_width {
- if (idx < buffer.len()) {
+ for _ in 0..(state.row_size / 4) {
+ for _ in 0..col_width {
+ if idx < buffer.len() {
let chr = buffer[idx];
+ if idx as u64 + state.seek == state.cursor {
+ write!(&mut hex_line, "\x1b[33m").unwrap();
+ write!(&mut ascii_line, "\x1b[33m").unwrap();
+ }
write!(&mut hex_line, "{:02x} ", chr).unwrap();
write!(&mut ascii_line, "{}", ascii_or_dot(chr) as char).unwrap();
+ if idx as u64 + state.seek == state.cursor {
+ write!(&mut hex_line, "\x1b[0m").unwrap();
+ write!(&mut ascii_line, "\x1b[0m").unwrap();
+ }
} else {
write!(&mut hex_line, " ").unwrap();
write!(&mut ascii_line, " ").unwrap();
@@ -113,7 +254,7 @@ fn render_interface(w: u16, h: u16, cursor: u64, buffer: &[u8]) {
write!(&mut hex_line, " ").unwrap();
}
- let lineaddr = ((i as u64) - 1) * (cols as u64 * 4);
+ let lineaddr = start as u64 + ((i as u64) - 1) * (state.row_size as u64);
let addr = if (lineaddr as usize) < buffer.len() {
format!("0x{:08x}:", lineaddr)
} else {
@@ -123,18 +264,21 @@ fn render_interface(w: u16, h: u16, cursor: u64, buffer: &[u8]) {
}
}
-fn populate_buf(arg: &String, cursor: u64, buffer: &mut [u8]) {
+fn populate_buf(arg: &String, seek: u64, count: u64) -> Vec<u8> {
match fs::File::open(&arg) {
Ok(mut fd) => {
/*..*/
- fd.seek(SeekFrom::Start(cursor));
- fd.read(buffer);
- for b in buffer.into_iter() {
- print!("{:02x}", b);
- }
- println!("");
+ fd.seek(SeekFrom::Start(seek)).unwrap();
+ fd
+ .bytes()
+ .take(count as usize)
+ .map(|r| r.unwrap()) // lazy
+ .collect()
},
Err(_) =>
- println!("Failed to open {}", arg)
+ {
+ println!("Failed to open {}", arg);
+ panic!();
+ }
}
}