Compare commits
3 Commits
71202752f8
...
90493fe11e
Author | SHA1 | Date |
---|---|---|
mars | 90493fe11e | |
mars | 0ba8cb0eb8 | |
mars | 4d448585ec |
412
src/main.rs
412
src/main.rs
|
@ -8,18 +8,20 @@ use alacritty_terminal::event_loop::{
|
|||
EventLoop as TermEventLoop, Msg as TermMsg, State as TermState,
|
||||
};
|
||||
use alacritty_terminal::sync::FairMutex;
|
||||
use alacritty_terminal::term::cell::{Cell, Flags as CellFlags};
|
||||
use alacritty_terminal::term::color::{Colors, Rgb};
|
||||
use alacritty_terminal::tty::Pty;
|
||||
use alacritty_terminal::Term;
|
||||
use mio_extras::channel::Sender as MioSender;
|
||||
use softbuffer::GraphicsContext;
|
||||
use std::borrow::Cow;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::mpsc::{channel, Receiver, Sender};
|
||||
use std::sync::Arc;
|
||||
use std::thread::JoinHandle;
|
||||
use winit::event::{Event, WindowEvent};
|
||||
use winit::event_loop::{ControlFlow, EventLoop};
|
||||
use winit::window::{Window, WindowBuilder};
|
||||
use winit::window::{Window, WindowBuilder, WindowId};
|
||||
|
||||
static NORMAL_FONT_DATA: &[u8] = include_bytes!("ter-u16n.bdf");
|
||||
static BOLD_FONT_DATA: &[u8] = include_bytes!("ter-u16b.bdf");
|
||||
|
@ -40,24 +42,67 @@ impl EventListener for TermListener {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct App {
|
||||
normal_font: bdf::Font,
|
||||
bold_font: bdf::Font,
|
||||
graphics: GraphicsContext<Window>,
|
||||
should_quit: bool,
|
||||
cell_width: usize,
|
||||
cell_height: usize,
|
||||
current_width: usize,
|
||||
current_height: usize,
|
||||
colors: Colors,
|
||||
term_loop: JoinHandle<(TermEventLoop<Pty, TermListener>, TermState)>,
|
||||
term_channel: MioSender<TermMsg>,
|
||||
term_events: Receiver<TermEvent>,
|
||||
term: Arc<FairMutex<Term<TermListener>>>,
|
||||
#[derive(Clone, Hash, PartialEq, Eq)]
|
||||
pub struct GlyphInfo {
|
||||
c: char,
|
||||
fg: u32,
|
||||
bg: u32,
|
||||
flags: CellFlags,
|
||||
}
|
||||
|
||||
impl App {
|
||||
pub fn new(window: Window) -> Self {
|
||||
pub struct GlyphCache {
|
||||
renderer: BitmapGlyphRenderer,
|
||||
colors: Colors,
|
||||
glyphs: HashMap<GlyphInfo, Box<[u32]>>,
|
||||
}
|
||||
|
||||
impl GlyphCache {
|
||||
pub fn new(colors: Colors) -> Self {
|
||||
Self {
|
||||
renderer: BitmapGlyphRenderer::new(),
|
||||
colors,
|
||||
glyphs: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lookup(&mut self, cell: &Cell) -> &[u32] {
|
||||
let glyph_info = GlyphInfo {
|
||||
c: cell.c,
|
||||
fg: self.color_to_u32(&cell.fg),
|
||||
bg: self.color_to_u32(&cell.bg),
|
||||
flags: cell.flags,
|
||||
};
|
||||
|
||||
self.glyphs
|
||||
.entry(glyph_info.clone())
|
||||
.or_insert_with(|| self.renderer.draw(&glyph_info).into_boxed_slice())
|
||||
}
|
||||
|
||||
// TODO deduplicate
|
||||
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 struct BitmapGlyphRenderer {
|
||||
normal_font: bdf::Font,
|
||||
bold_font: bdf::Font,
|
||||
cell_width: usize,
|
||||
cell_height: usize,
|
||||
}
|
||||
|
||||
impl BitmapGlyphRenderer {
|
||||
pub fn new() -> Self {
|
||||
let normal_font = bdf::read(NORMAL_FONT_DATA).unwrap();
|
||||
let bold_font = bdf::read(BOLD_FONT_DATA).unwrap();
|
||||
|
||||
|
@ -68,68 +113,85 @@ impl App {
|
|||
let cell_width = normal_font.bounds().width as usize;
|
||||
let cell_height = normal_font.bounds().height as usize;
|
||||
|
||||
let current_width = 2000;
|
||||
let current_height = 2000;
|
||||
|
||||
let term_size = alacritty_terminal::term::SizeInfo::new(
|
||||
current_width as f32,
|
||||
current_height as f32,
|
||||
cell_width as f32,
|
||||
cell_height as f32,
|
||||
0.0,
|
||||
0.0,
|
||||
false,
|
||||
);
|
||||
|
||||
let (sender, term_events) = channel();
|
||||
|
||||
let term_config = alacritty_terminal::config::Config {
|
||||
pty_config: PtyConfig {
|
||||
shell: Some(alacritty_terminal::config::Program::Just(
|
||||
"/usr/bin/fish".to_string(),
|
||||
)),
|
||||
working_directory: None,
|
||||
hold: false,
|
||||
},
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
alacritty_terminal::tty::setup_env(&term_config);
|
||||
|
||||
let term_listener = TermListener::new(sender.clone());
|
||||
|
||||
let term = Term::new(&term_config, term_size, term_listener);
|
||||
let mut colors = term.colors().to_owned();
|
||||
let term = FairMutex::new(term);
|
||||
let term = Arc::new(term);
|
||||
|
||||
let pty = alacritty_terminal::tty::new(&term_config.pty_config, &term_size, None).unwrap();
|
||||
|
||||
let term_listener = TermListener::new(sender);
|
||||
let term_loop = TermEventLoop::new(term.clone(), term_listener, pty, false, false);
|
||||
let term_channel = term_loop.channel();
|
||||
|
||||
Self::load_colors(&mut colors);
|
||||
|
||||
Self {
|
||||
normal_font,
|
||||
bold_font,
|
||||
graphics: unsafe { GraphicsContext::new(window).unwrap() },
|
||||
should_quit: false,
|
||||
cell_width,
|
||||
cell_height,
|
||||
current_width,
|
||||
current_height,
|
||||
term,
|
||||
term_channel,
|
||||
term_events,
|
||||
colors,
|
||||
term_loop: term_loop.spawn(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn should_quit(&self) -> bool {
|
||||
self.should_quit
|
||||
pub fn draw(&self, glyph: &GlyphInfo) -> Vec<u32> {
|
||||
let mut buf = Vec::with_capacity(self.cell_width * self.cell_height);
|
||||
|
||||
let font = if glyph.flags.contains(CellFlags::BOLD) {
|
||||
&self.bold_font
|
||||
} else {
|
||||
&self.normal_font
|
||||
};
|
||||
|
||||
if let Some(bitmap) = font.glyphs().get(&glyph.c) {
|
||||
let (bg, fg) = if glyph.flags.contains(CellFlags::INVERSE) {
|
||||
(glyph.fg, glyph.bg)
|
||||
} else {
|
||||
(glyph.bg, glyph.fg)
|
||||
};
|
||||
|
||||
for y in 0..self.cell_height {
|
||||
if y == self.cell_height >> 1 && glyph.flags.contains(CellFlags::STRIKEOUT)
|
||||
|| y == self.cell_height - 1 && glyph.flags.contains(CellFlags::UNDERLINE)
|
||||
{
|
||||
buf.resize(buf.len() + self.cell_width, fg);
|
||||
} else {
|
||||
for x in 0..self.cell_width {
|
||||
let color = if bitmap.get(x as u32, y as u32) {
|
||||
fg
|
||||
} else {
|
||||
bg
|
||||
};
|
||||
|
||||
buf.push(color);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
buf.resize(buf.capacity(), 0x00ff00ff);
|
||||
}
|
||||
|
||||
buf
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Graphics {
|
||||
glyph_cache: GlyphCache,
|
||||
width: usize,
|
||||
height: usize,
|
||||
cell_width: usize,
|
||||
cell_height: usize,
|
||||
colors: Colors,
|
||||
buffer: Vec<u32>,
|
||||
}
|
||||
|
||||
impl Graphics {
|
||||
pub fn new() -> Self {
|
||||
let width = 2000;
|
||||
let height = 2000;
|
||||
let buffer = vec![0u32; width * height];
|
||||
|
||||
let mut colors = Colors::default();
|
||||
Self::load_colors(&mut colors);
|
||||
|
||||
let glyph_cache = GlyphCache::new(colors.clone());
|
||||
|
||||
Self {
|
||||
width,
|
||||
height,
|
||||
cell_width: glyph_cache.renderer.cell_width,
|
||||
cell_height: glyph_cache.renderer.cell_height,
|
||||
glyph_cache,
|
||||
colors,
|
||||
buffer,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load_colors(color: &mut Colors) {
|
||||
|
@ -177,24 +239,37 @@ impl App {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn redraw(&mut self) {
|
||||
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 resize(&mut self, width: usize, height: usize) {
|
||||
self.width = width;
|
||||
self.height = height;
|
||||
|
||||
self.buffer.clear();
|
||||
self.buffer.resize(width * height, 0);
|
||||
}
|
||||
|
||||
pub fn redraw(&mut self, term: &Term<TermListener>, context: &mut GraphicsContext<Window>) {
|
||||
let (width, height) = {
|
||||
let size = self.graphics.window().inner_size();
|
||||
let size = context.window().inner_size();
|
||||
(size.width as usize, size.height as usize)
|
||||
};
|
||||
|
||||
if width != self.current_width || height != self.current_height {
|
||||
self.on_resize(width as u32, height as u32);
|
||||
}
|
||||
|
||||
let mut buffer = vec![0u32; (width * height) as usize];
|
||||
let term = self.term.lock();
|
||||
let content = term.renderable_content();
|
||||
|
||||
let mut missing_chars = std::collections::HashSet::new();
|
||||
|
||||
use alacritty_terminal::term::cell::Flags as CellFlags;
|
||||
|
||||
for cell in content.display_iter.into_iter() {
|
||||
let term_row = cell.point.line.0 as usize;
|
||||
let term_col = cell.point.column.0 as usize;
|
||||
|
@ -209,62 +284,23 @@ impl App {
|
|||
continue;
|
||||
}
|
||||
|
||||
let font = if cell.flags.contains(CellFlags::BOLD) {
|
||||
&self.bold_font
|
||||
} else {
|
||||
&self.normal_font
|
||||
};
|
||||
let glyph = self.glyph_cache.lookup(&cell);
|
||||
let px_row = term_row * self.cell_height * width;
|
||||
let mut px_start = px_row + term_col * self.cell_width;
|
||||
|
||||
if let Some(glyph) = font.glyphs().get(&cell.c) {
|
||||
let (bg, fg) = if cell.flags.contains(CellFlags::INVERSE) {
|
||||
(&cell.fg, &cell.bg)
|
||||
} else {
|
||||
(&cell.bg, &cell.fg)
|
||||
};
|
||||
for line in glyph.chunks_exact(self.cell_width) {
|
||||
let px_end = px_start + self.cell_width;
|
||||
|
||||
let bg = self.color_to_u32(&bg);
|
||||
let fg = self.color_to_u32(&fg);
|
||||
|
||||
let px_row = term_row * width * self.cell_height;
|
||||
let px_start = px_row + term_col * self.cell_width;
|
||||
|
||||
for y in 0..self.cell_height {
|
||||
let px_start = px_start + y * width;
|
||||
for x in 0..self.cell_width {
|
||||
let color = if glyph.get(x as u32, y as u32) {
|
||||
fg
|
||||
} else {
|
||||
bg
|
||||
};
|
||||
|
||||
buffer[px_start + x as usize] = color;
|
||||
}
|
||||
if px_end >= buffer.len() {
|
||||
println!("piss");
|
||||
break;
|
||||
}
|
||||
|
||||
if cell.flags.contains(CellFlags::STRIKEOUT) {
|
||||
let px_start = px_start + (self.cell_height >> 1) * width;
|
||||
|
||||
for x in 0..self.cell_width {
|
||||
buffer[px_start + x as usize] = fg;
|
||||
}
|
||||
}
|
||||
|
||||
if cell.flags.contains(CellFlags::UNDERLINE) {
|
||||
let px_start = px_start + (self.cell_height - 1) * width;
|
||||
|
||||
for x in 0..self.cell_width {
|
||||
buffer[px_start + x as usize] = fg;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
missing_chars.insert(cell.c);
|
||||
buffer[px_start..px_end].copy_from_slice(line);
|
||||
px_start += width;
|
||||
}
|
||||
}
|
||||
|
||||
if !missing_chars.is_empty() {
|
||||
log::debug!("Missing characters: {:?}", missing_chars);
|
||||
}
|
||||
|
||||
let term_row = content.cursor.point.line.0 as usize;
|
||||
let term_col = content.cursor.point.column.0 as usize;
|
||||
|
||||
|
@ -316,33 +352,91 @@ impl App {
|
|||
}
|
||||
}
|
||||
|
||||
self.graphics
|
||||
.set_buffer(&buffer, width as u16, height as u16);
|
||||
context.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,
|
||||
}),
|
||||
pub struct App {
|
||||
graphics: Graphics,
|
||||
graphics_context: GraphicsContext<Window>,
|
||||
should_quit: bool,
|
||||
term_loop: JoinHandle<(TermEventLoop<Pty, TermListener>, TermState)>,
|
||||
term_channel: MioSender<TermMsg>,
|
||||
term_events: Receiver<TermEvent>,
|
||||
term: Arc<FairMutex<Term<TermListener>>>,
|
||||
}
|
||||
|
||||
impl App {
|
||||
pub fn new(window: Window) -> Self {
|
||||
let graphics = Graphics::new();
|
||||
|
||||
let term_size = alacritty_terminal::term::SizeInfo::new(
|
||||
graphics.width as f32,
|
||||
graphics.height as f32,
|
||||
graphics.cell_width as f32,
|
||||
graphics.cell_height as f32,
|
||||
0.0,
|
||||
0.0,
|
||||
false,
|
||||
);
|
||||
|
||||
let (sender, term_events) = channel();
|
||||
|
||||
let term_config = alacritty_terminal::config::Config {
|
||||
pty_config: PtyConfig {
|
||||
shell: Some(alacritty_terminal::config::Program::Just(
|
||||
"/usr/bin/fish".to_string(),
|
||||
)),
|
||||
working_directory: None,
|
||||
hold: false,
|
||||
},
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
((rgb.r as u32) << 16) | ((rgb.g as u32) << 8) | (rgb.b as u32)
|
||||
alacritty_terminal::tty::setup_env(&term_config);
|
||||
|
||||
let term_listener = TermListener::new(sender.clone());
|
||||
|
||||
let term = Term::new(&term_config, term_size, term_listener);
|
||||
let term = FairMutex::new(term);
|
||||
let term = Arc::new(term);
|
||||
|
||||
let pty = alacritty_terminal::tty::new(&term_config.pty_config, &term_size, None).unwrap();
|
||||
|
||||
let term_listener = TermListener::new(sender);
|
||||
let term_loop = TermEventLoop::new(term.clone(), term_listener, pty, false, false);
|
||||
let term_channel = term_loop.channel();
|
||||
|
||||
Self {
|
||||
graphics,
|
||||
graphics_context: unsafe { GraphicsContext::new(window).unwrap() },
|
||||
should_quit: false,
|
||||
term,
|
||||
term_channel,
|
||||
term_events,
|
||||
term_loop: term_loop.spawn(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn should_quit(&self) -> bool {
|
||||
self.should_quit
|
||||
}
|
||||
|
||||
pub fn update(&mut self) {
|
||||
while let Ok(event) = self.term_events.try_recv() {
|
||||
match event {
|
||||
TermEvent::Exit => self.should_quit = true,
|
||||
TermEvent::Wakeup => self.redraw(),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn redraw(&mut self) {
|
||||
let term = self.term.lock();
|
||||
self.graphics.redraw(&term, &mut self.graphics_context);
|
||||
}
|
||||
|
||||
pub fn virtual_keycode_to_string(
|
||||
keycode: winit::event::VirtualKeyCode,
|
||||
) -> Option<&'static str> {
|
||||
|
@ -377,6 +471,7 @@ impl App {
|
|||
|
||||
match c {
|
||||
'\u{7f}' => {
|
||||
// We use a special escape code for the delete key.
|
||||
log::debug!("Ignoring.");
|
||||
return;
|
||||
}
|
||||
|
@ -397,8 +492,8 @@ impl App {
|
|||
let term_size = alacritty_terminal::term::SizeInfo::new(
|
||||
width as f32,
|
||||
height as f32,
|
||||
self.cell_width as f32,
|
||||
self.cell_height as f32,
|
||||
self.graphics.cell_width as f32,
|
||||
self.graphics.cell_height as f32,
|
||||
0.0,
|
||||
0.0,
|
||||
false,
|
||||
|
@ -406,8 +501,11 @@ impl App {
|
|||
|
||||
self.term_channel.send(TermMsg::Resize(term_size)).unwrap();
|
||||
|
||||
self.current_width = width as usize;
|
||||
self.current_height = height as usize;
|
||||
self.redraw();
|
||||
}
|
||||
|
||||
pub fn window_id(&self) -> WindowId {
|
||||
self.graphics_context.window().id()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -417,25 +515,17 @@ fn main() {
|
|||
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;
|
||||
*control_flow = ControlFlow::Poll;
|
||||
|
||||
app.update();
|
||||
|
||||
match event {
|
||||
Event::RedrawRequested(window_id) if window_id == app.graphics.window().id() => {
|
||||
counter += 1;
|
||||
|
||||
if counter > 30 {
|
||||
counter = 0;
|
||||
app.redraw();
|
||||
}
|
||||
Event::RedrawRequested(window_id) if window_id == app.window_id() => {
|
||||
app.redraw();
|
||||
}
|
||||
Event::MainEventsCleared => {
|
||||
app.graphics.window().request_redraw();
|
||||
app.update();
|
||||
}
|
||||
Event::WindowEvent { event, window_id } if window_id == app.graphics.window().id() => {
|
||||
Event::WindowEvent { event, window_id } if window_id == app.window_id() => {
|
||||
match event {
|
||||
WindowEvent::CloseRequested => {
|
||||
*control_flow = ControlFlow::Exit;
|
||||
|
|
Loading…
Reference in New Issue