init
This commit is contained in:
commit
4d0bf84355
6 changed files with 311 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
/target
|
132
Cargo.lock
generated
Normal file
132
Cargo.lock
generated
Normal file
|
@ -0,0 +1,132 @@
|
||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "1.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "2.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "getrandom"
|
||||||
|
version = "0.2.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"wasi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.155"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libredox"
|
||||||
|
version = "0.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3af92c55d7d839293953fcd0fda5ecfe93297cfde6ffbdec13b41d99c0ba6607"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.6.0",
|
||||||
|
"libc",
|
||||||
|
"redox_syscall",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "matrixed"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"rand",
|
||||||
|
"termion",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "numtoa"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ppv-lite86"
|
||||||
|
version = "0.2.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand"
|
||||||
|
version = "0.8.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"rand_chacha",
|
||||||
|
"rand_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_chacha"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||||
|
dependencies = [
|
||||||
|
"ppv-lite86",
|
||||||
|
"rand_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.6.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "redox_syscall"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 1.3.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "redox_termios"
|
||||||
|
version = "0.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "20145670ba436b55d91fc92d25e71160fbfbdd57831631c8d7d36377a476f1cb"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "termion"
|
||||||
|
version = "4.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1ccce68e518d1173e80876edd54760b60b792750d0cab6444a79101c6ea03848"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"libredox",
|
||||||
|
"numtoa",
|
||||||
|
"redox_termios",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasi"
|
||||||
|
version = "0.11.0+wasi-snapshot-preview1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
8
Cargo.toml
Normal file
8
Cargo.toml
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
[package]
|
||||||
|
name = "matrixed"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
rand = "0.8.5"
|
||||||
|
termion = "4.0.2"
|
3
README.md
Normal file
3
README.md
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
# Matrixed
|
||||||
|
|
||||||
|
Minimal cmatrix-like program that prints text from stdin.
|
3
rustfmt.toml
Normal file
3
rustfmt.toml
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
|
||||||
|
hard_tabs = true
|
||||||
|
max_width = 120
|
164
src/main.rs
Normal file
164
src/main.rs
Normal file
|
@ -0,0 +1,164 @@
|
||||||
|
use std::io::{stdin, stdout, BufRead, Write};
|
||||||
|
use std::iter::repeat;
|
||||||
|
use std::sync::mpsc::{self, Sender};
|
||||||
|
use std::thread;
|
||||||
|
use std::time::Duration;
|
||||||
|
use std::{collections::VecDeque, sync::mpsc::Receiver};
|
||||||
|
|
||||||
|
use rand::{seq::IteratorRandom, thread_rng};
|
||||||
|
use termion::terminal_size;
|
||||||
|
use termion::{clear, cursor::Goto};
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut screen = Screen::default();
|
||||||
|
|
||||||
|
let lines = reading_thread();
|
||||||
|
let mut queue = None;
|
||||||
|
|
||||||
|
let mut stdout = stdout().lock();
|
||||||
|
loop {
|
||||||
|
// TODO : check for size
|
||||||
|
let size = get_term_size();
|
||||||
|
if screen.dim != size {
|
||||||
|
screen.resize(size.0, size.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if queue.is_none() {
|
||||||
|
if let Ok(received) = lines.try_recv() {
|
||||||
|
queue = Some(received);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some((text, ok)) = &queue {
|
||||||
|
let success = screen.append(text.clone());
|
||||||
|
if success {
|
||||||
|
ok.send(()).unwrap();
|
||||||
|
queue.take();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
screen.step();
|
||||||
|
stdout.write_all(screen.draw().as_bytes()).unwrap();
|
||||||
|
stdout.flush().unwrap();
|
||||||
|
thread::sleep(Duration::from_millis(100));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reading_thread() -> Receiver<(String, Sender<()>)> {
|
||||||
|
let (cmd_tx, cmd_rx) = mpsc::channel();
|
||||||
|
thread::spawn(move || {
|
||||||
|
let mut stdin = stdin().lock();
|
||||||
|
loop {
|
||||||
|
let mut line = String::new();
|
||||||
|
stdin.read_line(&mut line).unwrap();
|
||||||
|
let (tx, rx) = mpsc::channel();
|
||||||
|
cmd_tx.send((line, tx)).unwrap();
|
||||||
|
rx.recv().unwrap();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
cmd_rx
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
struct Screen {
|
||||||
|
dim: (usize, usize),
|
||||||
|
lines: Vec<ScreenLine>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Screen {
|
||||||
|
pub fn resize(&mut self, width: usize, height: usize) {
|
||||||
|
let (old_width, _) = self.dim;
|
||||||
|
let width_difference = width.saturating_sub(old_width);
|
||||||
|
let new_lines = repeat(ScreenLine::default()).take(width_difference);
|
||||||
|
self.lines.truncate(width);
|
||||||
|
self.lines.extend(new_lines);
|
||||||
|
self.dim = (width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn append(&mut self, text: String) -> bool {
|
||||||
|
if let Some(available) = self.find_available_line() {
|
||||||
|
available.append(text);
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn draw(&self) -> String {
|
||||||
|
let clear = clear::All;
|
||||||
|
let mut sequence = format!("{clear}");
|
||||||
|
let (_, height) = self.dim;
|
||||||
|
for (x, line) in self.lines.iter().enumerate() {
|
||||||
|
for text in &line.content {
|
||||||
|
for (pos, letter) in text.content.chars().enumerate() {
|
||||||
|
let y = pos as isize + text.progress;
|
||||||
|
if y < 0 || y >= (height - 1) as isize {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let goto = Goto((x + 1) as u16, (y + 1) as u16);
|
||||||
|
sequence += &format!("{goto}{letter}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let goto_topleft = Goto(1, 1);
|
||||||
|
sequence += &format!("{goto_topleft}");
|
||||||
|
sequence
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn step(&mut self) {
|
||||||
|
let (_, height) = self.dim;
|
||||||
|
for line in &mut self.lines.iter_mut() {
|
||||||
|
line.step(height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_available_line(&mut self) -> Option<&mut ScreenLine> {
|
||||||
|
self.lines
|
||||||
|
.iter_mut()
|
||||||
|
.filter(|l| l.is_available())
|
||||||
|
.choose(&mut thread_rng())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default)]
|
||||||
|
struct ScreenLine {
|
||||||
|
content: VecDeque<Text>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ScreenLine {
|
||||||
|
pub fn is_available(&self) -> bool {
|
||||||
|
let highest = self.content.back();
|
||||||
|
highest.map(|t| t.progress >= 1).unwrap_or(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn append(&mut self, text: String) {
|
||||||
|
let content = text;
|
||||||
|
let progress = -(content.len() as isize);
|
||||||
|
self.content.push_back(Text { content, progress });
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn step(&mut self, height: usize) {
|
||||||
|
for text in &mut self.content {
|
||||||
|
text.progress += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
while let Some(last) = self.content.front() {
|
||||||
|
if last.progress > height as isize {
|
||||||
|
self.content.pop_front();
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
struct Text {
|
||||||
|
pub content: String,
|
||||||
|
pub progress: isize,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_term_size() -> (usize, usize) {
|
||||||
|
let (width, height) = terminal_size().unwrap_or((80, 40));
|
||||||
|
(width as usize, height as usize)
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue