Initial commit
This commit is contained in:
commit
c2e202a743
|
@ -0,0 +1 @@
|
|||
/target
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,13 @@
|
|||
[package]
|
||||
name = "piss"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
alacritty_terminal = "0.16"
|
||||
bdf = { git = "https://github.com/meh/rust-bdf" }
|
||||
env_logger = "0.9"
|
||||
log = "0.4"
|
||||
mio-extras = "2"
|
||||
softbuffer = { git = "https://github.com/john01dav/softbuffer" }
|
||||
winit = "0.27"
|
|
@ -0,0 +1,338 @@
|
|||
use alacritty_terminal::ansi::{Color, NamedColor};
|
||||
use alacritty_terminal::config::PtyConfig;
|
||||
use alacritty_terminal::event::{Event as TermEvent, EventListener};
|
||||
use alacritty_terminal::event_loop::{
|
||||
EventLoop as TermEventLoop, Msg as TermMsg, State as TermState,
|
||||
};
|
||||
use alacritty_terminal::sync::FairMutex;
|
||||
use alacritty_terminal::term::color::{Colors, Rgb};
|
||||
use alacritty_terminal::tty::Pty;
|
||||
use alacritty_terminal::Term;
|
||||
use mio_extras::channel::Sender;
|
||||
use softbuffer::GraphicsContext;
|
||||
use std::borrow::Cow;
|
||||
use std::sync::Arc;
|
||||
use std::thread::JoinHandle;
|
||||
use winit::event::{Event, WindowEvent};
|
||||
use winit::event_loop::{ControlFlow, EventLoop};
|
||||
use winit::window::{Window, WindowBuilder};
|
||||
|
||||
const WINDOW_WIDTH: usize = 900;
|
||||
const WINDOW_HEIGHT: usize = 600;
|
||||
|
||||
static FONT_DATA: &[u8] = include_bytes!("/usr/share/fonts/local/TamzenForPowerline10x20r.bdf");
|
||||
|
||||
pub struct TermListener {}
|
||||
|
||||
impl EventListener for TermListener {
|
||||
fn send_event(&self, event: TermEvent) {}
|
||||
}
|
||||
|
||||
pub struct App {
|
||||
font: bdf::Font,
|
||||
graphics: GraphicsContext<Window>,
|
||||
cell_width: usize,
|
||||
cell_height: usize,
|
||||
colors: Colors,
|
||||
term_loop: JoinHandle<(TermEventLoop<Pty, TermListener>, TermState)>,
|
||||
term_channel: Sender<TermMsg>,
|
||||
term: Arc<FairMutex<Term<TermListener>>>,
|
||||
}
|
||||
|
||||
impl App {
|
||||
pub fn new(window: Window) -> Self {
|
||||
let font = bdf::read(FONT_DATA).unwrap();
|
||||
|
||||
let cell_width = font.bounds().width as usize;
|
||||
let cell_height = font.bounds().height as usize;
|
||||
|
||||
let term_size = alacritty_terminal::term::SizeInfo::new(
|
||||
WINDOW_WIDTH as f32,
|
||||
WINDOW_HEIGHT as f32,
|
||||
cell_width as f32,
|
||||
cell_height as f32,
|
||||
0.0,
|
||||
0.0,
|
||||
false,
|
||||
);
|
||||
|
||||
let term_config = Default::default();
|
||||
let term_events = TermListener {};
|
||||
|
||||
let pty_config = PtyConfig {
|
||||
shell: Some(alacritty_terminal::config::Program::Just(
|
||||
"/usr/bin/fish".to_string(),
|
||||
)),
|
||||
working_directory: None,
|
||||
hold: false,
|
||||
};
|
||||
|
||||
let term = Term::new(&term_config, term_size, term_events);
|
||||
let mut colors = term.colors().to_owned();
|
||||
let term = FairMutex::new(term);
|
||||
let term = Arc::new(term);
|
||||
|
||||
let pty = alacritty_terminal::tty::new(&pty_config, &term_size, None).unwrap();
|
||||
|
||||
let term_events = TermListener {};
|
||||
let term_loop = TermEventLoop::new(term.clone(), term_events, pty, false, false);
|
||||
let term_channel = term_loop.channel();
|
||||
|
||||
Self::load_colors(&mut colors);
|
||||
|
||||
Self {
|
||||
font,
|
||||
graphics: unsafe { GraphicsContext::new(window).unwrap() },
|
||||
cell_width,
|
||||
cell_height,
|
||||
term,
|
||||
term_channel,
|
||||
colors,
|
||||
term_loop: term_loop.spawn(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load_colors(color: &mut Colors) {
|
||||
use NamedColor::*;
|
||||
|
||||
let maps = [
|
||||
(Black, Rgb { r: 0, g: 0, b: 0 }),
|
||||
(Red, Rgb { r: 255, g: 0, b: 0 }),
|
||||
(Green, Rgb { r: 0, g: 255, b: 0 }),
|
||||
(Blue, Rgb { r: 0, g: 0, b: 255 }),
|
||||
(
|
||||
Yellow,
|
||||
Rgb {
|
||||
r: 255,
|
||||
g: 255,
|
||||
b: 0,
|
||||
},
|
||||
),
|
||||
(
|
||||
Magenta,
|
||||
Rgb {
|
||||
r: 255,
|
||||
g: 0,
|
||||
b: 255,
|
||||
},
|
||||
),
|
||||
(
|
||||
Cyan,
|
||||
Rgb {
|
||||
r: 0,
|
||||
g: 255,
|
||||
b: 255,
|
||||
},
|
||||
),
|
||||
(
|
||||
White,
|
||||
Rgb {
|
||||
r: 255,
|
||||
g: 255,
|
||||
b: 255,
|
||||
},
|
||||
),
|
||||
];
|
||||
|
||||
for map in maps.iter() {
|
||||
color[map.0] = Some(map.1);
|
||||
}
|
||||
|
||||
let dupes = [
|
||||
(Background, Black),
|
||||
(Foreground, White),
|
||||
(BrightBlack, Black),
|
||||
(BrightRed, Red),
|
||||
(BrightGreen, Green),
|
||||
(BrightYellow, Yellow),
|
||||
(BrightBlue, Blue),
|
||||
(BrightMagenta, Magenta),
|
||||
(BrightCyan, Cyan),
|
||||
(BrightWhite, White),
|
||||
];
|
||||
|
||||
for (dst, src) in dupes.iter() {
|
||||
color[*dst] = color[*src];
|
||||
}
|
||||
}
|
||||
|
||||
pub fn redraw(&mut self) {
|
||||
/*let (width, height) = {
|
||||
let size = self.graphics.window().inner_size();
|
||||
(size.width as usize, size.height as usize)
|
||||
};*/
|
||||
|
||||
let (width, height) = (WINDOW_WIDTH, WINDOW_HEIGHT);
|
||||
|
||||
let mut buffer = vec![0u32; (width * height) as usize];
|
||||
let term = self.term.lock();
|
||||
let content = term.renderable_content();
|
||||
|
||||
let mut fg_num = 0;
|
||||
|
||||
for cell in content.display_iter.into_iter() {
|
||||
if let Some(glyph) = self.font.glyphs().get(&cell.c) {
|
||||
// println!("cell: {:?}", cell);
|
||||
|
||||
let term_row = cell.point.line.0 as usize;
|
||||
let term_col = cell.point.column.0 as usize;
|
||||
|
||||
let bg = self.color_to_u32(&cell.bg);
|
||||
let fg = self.color_to_u32(&cell.fg);
|
||||
|
||||
let mut px_row = term_row * width * self.cell_height;
|
||||
|
||||
if px_row >= buffer.len() {
|
||||
continue;
|
||||
}
|
||||
|
||||
for y in 0..self.cell_height {
|
||||
let px_start = px_row + term_col * self.cell_width;
|
||||
for x in 0..self.cell_width {
|
||||
let color = if glyph.get(x as u32, y as u32) {
|
||||
fg_num += 1;
|
||||
fg
|
||||
} else {
|
||||
bg
|
||||
};
|
||||
|
||||
let off = px_start + x as usize;
|
||||
|
||||
// if off < buffer.len() {
|
||||
buffer[off] = color;
|
||||
// }
|
||||
}
|
||||
|
||||
px_row += width;
|
||||
}
|
||||
} else {
|
||||
println!("missing char: {}", cell.c);
|
||||
}
|
||||
}
|
||||
|
||||
log::debug!("FG num: {}", fg_num);
|
||||
|
||||
self.graphics
|
||||
.set_buffer(&buffer, width as u16, height as u16);
|
||||
}
|
||||
|
||||
pub fn color_to_u32(&self, color: &Color) -> u32 {
|
||||
let rgb = match color {
|
||||
Color::Named(name) => self.colors[*name].unwrap(),
|
||||
Color::Spec(rgb) => *rgb,
|
||||
Color::Indexed(index) => self.colors[*index as usize].unwrap_or(Rgb {
|
||||
r: 255,
|
||||
g: 0,
|
||||
b: 255,
|
||||
}),
|
||||
};
|
||||
|
||||
((rgb.r as u32) << 16) | ((rgb.g as u32) << 8) | (rgb.b as u32)
|
||||
}
|
||||
|
||||
pub fn update(&mut self) {}
|
||||
|
||||
pub fn on_keyboard_input(&mut self, input: &winit::event::KeyboardInput) {
|
||||
if input.state == winit::event::ElementState::Pressed {
|
||||
if let Some(keycode) = input.virtual_keycode {
|
||||
if let Some(c) = virtual_keycode_to_byte(keycode) {
|
||||
let bytes = [c];
|
||||
let cow = Cow::Owned(bytes.into());
|
||||
self.term_channel.send(TermMsg::Input(cow)).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn virtual_keycode_to_byte(keycode: winit::event::VirtualKeyCode) -> Option<u8> {
|
||||
use winit::event::VirtualKeyCode::*;
|
||||
match keycode {
|
||||
A => Some(b'a'),
|
||||
B => Some(b'b'),
|
||||
C => Some(b'c'),
|
||||
D => Some(b'd'),
|
||||
E => Some(b'e'),
|
||||
F => Some(b'f'),
|
||||
G => Some(b'g'),
|
||||
H => Some(b'h'),
|
||||
I => Some(b'i'),
|
||||
J => Some(b'j'),
|
||||
K => Some(b'k'),
|
||||
L => Some(b'l'),
|
||||
M => Some(b'm'),
|
||||
N => Some(b'n'),
|
||||
O => Some(b'o'),
|
||||
P => Some(b'p'),
|
||||
Q => Some(b'q'),
|
||||
R => Some(b'r'),
|
||||
S => Some(b's'),
|
||||
T => Some(b't'),
|
||||
U => Some(b'u'),
|
||||
V => Some(b'v'),
|
||||
W => Some(b'w'),
|
||||
X => Some(b'x'),
|
||||
Y => Some(b'y'),
|
||||
Z => Some(b'z'),
|
||||
Key1 => Some(b'1'),
|
||||
Key2 => Some(b'2'),
|
||||
Key3 => Some(b'3'),
|
||||
Key4 => Some(b'4'),
|
||||
Key5 => Some(b'5'),
|
||||
Key6 => Some(b'6'),
|
||||
Key7 => Some(b'7'),
|
||||
Key8 => Some(b'8'),
|
||||
Key9 => Some(b'('),
|
||||
Key0 => Some(b')'),
|
||||
Apostrophe => Some(b'\''),
|
||||
Period => Some(b'.'),
|
||||
Escape => Some(0x1b),
|
||||
Backslash => Some(b'\\'),
|
||||
Colon => Some(b':'),
|
||||
Return => Some(b'\r'),
|
||||
Back => Some(0x08),
|
||||
Tab => Some(b'\t'),
|
||||
Space => Some(b' '),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
env_logger::init();
|
||||
|
||||
let event_loop = EventLoop::new();
|
||||
let window = WindowBuilder::new().build(&event_loop).unwrap();
|
||||
let mut app = App::new(window);
|
||||
let mut counter = 0;
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
*control_flow = ControlFlow::Wait;
|
||||
|
||||
match event {
|
||||
Event::RedrawRequested(window_id) if window_id == app.graphics.window().id() => {
|
||||
counter += 1;
|
||||
|
||||
if counter > 30 {
|
||||
counter = 0;
|
||||
app.redraw();
|
||||
}
|
||||
}
|
||||
Event::MainEventsCleared => {
|
||||
app.graphics.window().request_redraw();
|
||||
app.update();
|
||||
}
|
||||
Event::WindowEvent { event, window_id } if window_id == app.graphics.window().id() => {
|
||||
match event {
|
||||
WindowEvent::CloseRequested => {
|
||||
*control_flow = ControlFlow::Exit;
|
||||
}
|
||||
WindowEvent::KeyboardInput { input, .. } => {
|
||||
app.on_keyboard_input(&input);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
});
|
||||
}
|
Loading…
Reference in New Issue