Compare commits
2 Commits
ca50d28227
...
acefda1d28
Author | SHA1 | Date |
---|---|---|
mars | acefda1d28 | |
mars | 3fa469537f |
|
@ -0,0 +1,438 @@
|
|||
use alacritty_terminal::ansi::{Color, NamedColor};
|
||||
use alacritty_terminal::term::cell::{Cell, Flags as CellFlags};
|
||||
use alacritty_terminal::term::color::{Colors, Rgb};
|
||||
use alacritty_terminal::Term;
|
||||
use softbuffer::GraphicsContext;
|
||||
use std::collections::HashMap;
|
||||
use winit::window::Window;
|
||||
|
||||
static NORMAL_FONT_DATA: &[u8] = include_bytes!("ter-u16n.bdf");
|
||||
static BOLD_FONT_DATA: &[u8] = include_bytes!("ter-u16b.bdf");
|
||||
|
||||
use super::TermListener;
|
||||
|
||||
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct Point(pub usize, pub usize);
|
||||
|
||||
impl Point {
|
||||
pub const ZERO: Self = Self(0, 0);
|
||||
pub const ONE: Self = Self(1, 1);
|
||||
}
|
||||
|
||||
impl std::ops::Add for Point {
|
||||
type Output = Self;
|
||||
|
||||
fn add(self, rhs: Self) -> Self {
|
||||
Self(self.0 + rhs.0, self.1 + rhs.1)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Mul for Point {
|
||||
type Output = Self;
|
||||
|
||||
fn mul(self, rhs: Self) -> Self {
|
||||
Self(self.0 * rhs.0, self.1 * rhs.1)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Canvas {
|
||||
pub width: usize,
|
||||
pub height: usize,
|
||||
pub buffer: Box<[u32]>,
|
||||
}
|
||||
|
||||
impl Canvas {
|
||||
#[inline]
|
||||
pub fn new(width: usize, height: usize) -> Self {
|
||||
Self {
|
||||
width,
|
||||
height,
|
||||
buffer: vec![0; width * height].into_boxed_slice(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn clip(&self, xy: Point) -> Point {
|
||||
Point(xy.0.min(self.width - 1), xy.1.min(self.height - 1))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn draw_rect(&mut self, color: u32, tl: Point, br: Point) {
|
||||
if tl.0 >= self.width || tl.1 >= self.height {
|
||||
return;
|
||||
}
|
||||
|
||||
let br = self.clip(br);
|
||||
|
||||
unsafe {
|
||||
self.draw_rect_unchecked(color, tl, br);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub unsafe fn draw_rect_unchecked(&mut self, color: u32, tl: Point, br: Point) {
|
||||
let row = tl.1 * self.width;
|
||||
let mut dst_start = row + tl.0;
|
||||
let mut dst_end = row + br.0;
|
||||
|
||||
for _ in tl.1..br.1 {
|
||||
let dst = dst_start..dst_end;
|
||||
self.buffer.get_unchecked_mut(dst).fill(color);
|
||||
dst_start += self.width;
|
||||
dst_end += self.width;
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn blit(&mut self, xy: Point, src: &Canvas) {
|
||||
let size = Point(src.width, src.height);
|
||||
self.blit_from_slice(xy, &src.buffer, size);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn blit_from_slice(&mut self, xy: Point, src_buffer: &[u32], src_size: Point) {
|
||||
if xy.0 < self.width && xy.1 < self.height {
|
||||
let cw = (self.width - xy.0 - 1).min(src_size.0);
|
||||
let ch = (self.height - xy.1 - 1).min(src_size.1);
|
||||
|
||||
let dst_row = xy.1 * self.width;
|
||||
let mut dst_start = dst_row + xy.0;
|
||||
let mut dst_end = dst_start + cw;
|
||||
|
||||
let mut src_start = 0;
|
||||
let mut src_end = cw;
|
||||
|
||||
for _ in 0..ch {
|
||||
unsafe {
|
||||
let dst_buf = self.buffer.get_unchecked_mut(dst_start..dst_end);
|
||||
let src_buf = src_buffer.get_unchecked(src_start..src_end);
|
||||
dst_buf.copy_from_slice(src_buf);
|
||||
dst_start += self.width;
|
||||
dst_end += self.width;
|
||||
src_start += src_size.0;
|
||||
src_end += src_size.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Hash, PartialEq, Eq)]
|
||||
pub struct GlyphInfo {
|
||||
c: char,
|
||||
fg: u32,
|
||||
bg: u32,
|
||||
flags: CellFlags,
|
||||
}
|
||||
|
||||
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 cell_info(&self, cell: &Cell) -> GlyphInfo {
|
||||
GlyphInfo {
|
||||
c: cell.c,
|
||||
fg: self.color_to_u32(&cell.fg),
|
||||
bg: self.color_to_u32(&cell.bg),
|
||||
flags: cell.flags,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lookup(&mut self, glyph: &GlyphInfo) -> &[u32] {
|
||||
self.glyphs
|
||||
.entry(glyph.clone())
|
||||
.or_insert_with(|| self.renderer.draw(glyph).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();
|
||||
|
||||
if normal_font.bounds() != bold_font.bounds() {
|
||||
panic!("Normal and bold font bounds do not match!");
|
||||
}
|
||||
|
||||
let cell_width = normal_font.bounds().width as usize;
|
||||
let cell_height = normal_font.bounds().height as usize;
|
||||
|
||||
Self {
|
||||
normal_font,
|
||||
bold_font,
|
||||
cell_width,
|
||||
cell_height,
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
pub canvas: Canvas,
|
||||
pub cell_width: usize,
|
||||
pub cell_height: usize,
|
||||
pub colors: Colors,
|
||||
glyph_cache: GlyphCache,
|
||||
cell_cache: HashMap<Point, GlyphInfo>,
|
||||
}
|
||||
|
||||
impl Graphics {
|
||||
pub fn new() -> Self {
|
||||
let canvas = Canvas::new(2000, 2000);
|
||||
|
||||
let mut colors = Colors::default();
|
||||
Self::load_colors(&mut colors);
|
||||
|
||||
let glyph_cache = GlyphCache::new(colors.clone());
|
||||
let cell_cache = Default::default();
|
||||
|
||||
Self {
|
||||
canvas,
|
||||
cell_width: glyph_cache.renderer.cell_width,
|
||||
cell_height: glyph_cache.renderer.cell_height,
|
||||
glyph_cache,
|
||||
cell_cache,
|
||||
colors,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load_colors(color: &mut Colors) {
|
||||
use NamedColor::*;
|
||||
|
||||
let hex = |hex: u32| -> Rgb {
|
||||
Rgb {
|
||||
r: (hex >> 16) as u8,
|
||||
g: (hex >> 8) as u8,
|
||||
b: hex as u8,
|
||||
}
|
||||
};
|
||||
|
||||
// Rose Pine theme
|
||||
let maps = [
|
||||
(Foreground, hex(0xe0def4)),
|
||||
(Background, hex(0x191724)),
|
||||
(Black, hex(0x6e6a86)),
|
||||
(Red, hex(0xeb6f92)),
|
||||
(Green, hex(0x7fb59f)),
|
||||
(Yellow, hex(0xf6c177)),
|
||||
(Blue, hex(0x31748f)),
|
||||
(Magenta, hex(0xc4a7e7)),
|
||||
(Cyan, hex(0x9ccfd8)),
|
||||
(White, hex(0xe0def4)),
|
||||
];
|
||||
|
||||
for map in maps.iter() {
|
||||
color[map.0] = Some(map.1);
|
||||
}
|
||||
|
||||
let dupes = [
|
||||
(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 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.canvas = Canvas::new(width, height);
|
||||
self.cell_cache.clear();
|
||||
}
|
||||
|
||||
pub fn redraw(&mut self, term: &Term<TermListener>, context: &mut GraphicsContext<Window>) {
|
||||
let start = std::time::Instant::now();
|
||||
|
||||
let (width, height) = {
|
||||
let size = context.window().inner_size();
|
||||
(size.width as usize, size.height as usize)
|
||||
};
|
||||
|
||||
if width != self.canvas.width || height != self.canvas.height {
|
||||
self.resize(width, height);
|
||||
}
|
||||
|
||||
let cursor_color = Color::Named(NamedColor::Foreground);
|
||||
let cursor_color = self.color_to_u32(&cursor_color);
|
||||
let cell_size = Point(self.cell_width, self.cell_height);
|
||||
let content = term.renderable_content();
|
||||
|
||||
for cell in content.display_iter.into_iter() {
|
||||
let term_col = cell.point.column.0 as usize;
|
||||
let term_row = cell.point.line.0 as usize;
|
||||
let term_coords = Point(term_col, term_row);
|
||||
|
||||
if (term_row + 1) * self.cell_height >= height
|
||||
|| (term_col + 1) * self.cell_width >= width
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if cell.flags.contains(CellFlags::HIDDEN) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let glyph_info = self.glyph_cache.cell_info(&cell);
|
||||
|
||||
if self.cell_cache.get(&term_coords) == Some(&glyph_info) {
|
||||
continue;
|
||||
} else {
|
||||
self.cell_cache.insert(term_coords, glyph_info.clone());
|
||||
}
|
||||
|
||||
let xy = term_coords * cell_size;
|
||||
let glyph = self.glyph_cache.lookup(&glyph_info);
|
||||
self.canvas.blit_from_slice(xy, glyph, cell_size);
|
||||
}
|
||||
|
||||
let term_col = content.cursor.point.column.0 as usize;
|
||||
let term_row = content.cursor.point.line.0 as usize;
|
||||
let term_coords = Point(term_col, term_row);
|
||||
|
||||
if (term_row + 1) * self.cell_height < height && (term_col + 1) * self.cell_width < width {
|
||||
let mut px_row = term_row * width * self.cell_height + term_col * self.cell_width;
|
||||
|
||||
use alacritty_terminal::ansi::CursorShape;
|
||||
match content.cursor.shape {
|
||||
CursorShape::Block => {
|
||||
let tl = term_coords * cell_size;
|
||||
let br = tl + cell_size;
|
||||
self.canvas.draw_rect(cursor_color, tl, br);
|
||||
}
|
||||
CursorShape::Beam => {
|
||||
let tl = term_coords * cell_size;
|
||||
let br = tl + Point(cell_size.0, 2);
|
||||
self.canvas.draw_rect(cursor_color, tl, br);
|
||||
}
|
||||
CursorShape::Underline => {
|
||||
let mut tl = term_coords * cell_size;
|
||||
let br = tl + cell_size;
|
||||
tl.0 += cell_size.0 - 2;
|
||||
self.canvas.draw_rect(cursor_color, tl, br);
|
||||
}
|
||||
CursorShape::HollowBlock => {
|
||||
// TODO Canvas::draw_hollow_rect()
|
||||
let buffer = &mut self.canvas.buffer;
|
||||
|
||||
for x in 0..self.cell_width {
|
||||
buffer[px_row + x as usize] = cursor_color;
|
||||
}
|
||||
|
||||
for _y in 0..(self.cell_height - 1) {
|
||||
buffer[px_row] = cursor_color;
|
||||
buffer[px_row + self.cell_width - 1] = cursor_color;
|
||||
px_row += width;
|
||||
}
|
||||
|
||||
for x in 0..self.cell_width {
|
||||
buffer[px_row + x as usize] = cursor_color;
|
||||
}
|
||||
}
|
||||
CursorShape::Hidden => {}
|
||||
}
|
||||
|
||||
if content.cursor.shape != CursorShape::Hidden {
|
||||
self.cell_cache.remove(&term_coords);
|
||||
}
|
||||
}
|
||||
|
||||
let copy_start = std::time::Instant::now();
|
||||
context.set_buffer(&self.canvas.buffer, width as u16, height as u16);
|
||||
log::debug!("buffer copy time: {:?}", copy_start.elapsed());
|
||||
|
||||
log::debug!("redraw() time: {:?}", start.elapsed());
|
||||
}
|
||||
}
|
358
src/main.rs
358
src/main.rs
|
@ -1,21 +1,18 @@
|
|||
// Copyright (c) 2022 Marceline Cramer
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
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::cell::{Cell, Flags as CellFlags};
|
||||
use alacritty_terminal::term::color::{Colors, Rgb};
|
||||
use alacritty_terminal::term::color::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;
|
||||
|
@ -23,8 +20,9 @@ use winit::event::{Event, WindowEvent};
|
|||
use winit::event_loop::{ControlFlow, EventLoop};
|
||||
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");
|
||||
pub mod graphics;
|
||||
|
||||
use graphics::Graphics;
|
||||
|
||||
pub struct TermListener {
|
||||
sender: Sender<TermEvent>,
|
||||
|
@ -42,350 +40,6 @@ impl EventListener for TermListener {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Hash, PartialEq, Eq)]
|
||||
pub struct GlyphInfo {
|
||||
c: char,
|
||||
fg: u32,
|
||||
bg: u32,
|
||||
flags: CellFlags,
|
||||
}
|
||||
|
||||
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 cell_info(&self, cell: &Cell) -> GlyphInfo {
|
||||
GlyphInfo {
|
||||
c: cell.c,
|
||||
fg: self.color_to_u32(&cell.fg),
|
||||
bg: self.color_to_u32(&cell.bg),
|
||||
flags: cell.flags,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lookup(&mut self, glyph: &GlyphInfo) -> &[u32] {
|
||||
self.glyphs
|
||||
.entry(glyph.clone())
|
||||
.or_insert_with(|| self.renderer.draw(glyph).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();
|
||||
|
||||
if normal_font.bounds() != bold_font.bounds() {
|
||||
panic!("Normal and bold font bounds do not match!");
|
||||
}
|
||||
|
||||
let cell_width = normal_font.bounds().width as usize;
|
||||
let cell_height = normal_font.bounds().height as usize;
|
||||
|
||||
Self {
|
||||
normal_font,
|
||||
bold_font,
|
||||
cell_width,
|
||||
cell_height,
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
cell_cache: HashMap<(usize, usize), GlyphInfo>,
|
||||
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());
|
||||
let cell_cache = Default::default();
|
||||
|
||||
Self {
|
||||
width,
|
||||
height,
|
||||
cell_width: glyph_cache.renderer.cell_width,
|
||||
cell_height: glyph_cache.renderer.cell_height,
|
||||
glyph_cache,
|
||||
cell_cache,
|
||||
colors,
|
||||
buffer,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load_colors(color: &mut Colors) {
|
||||
use NamedColor::*;
|
||||
|
||||
let hex = |hex: u32| -> Rgb {
|
||||
Rgb {
|
||||
r: (hex >> 16) as u8,
|
||||
g: (hex >> 8) as u8,
|
||||
b: hex as u8,
|
||||
}
|
||||
};
|
||||
|
||||
// Rose Pine theme
|
||||
let maps = [
|
||||
(Foreground, hex(0xe0def4)),
|
||||
(Background, hex(0x191724)),
|
||||
(Black, hex(0x6e6a86)),
|
||||
(Red, hex(0xeb6f92)),
|
||||
(Green, hex(0x7fb59f)),
|
||||
(Yellow, hex(0xf6c177)),
|
||||
(Blue, hex(0x31748f)),
|
||||
(Magenta, hex(0xc4a7e7)),
|
||||
(Cyan, hex(0x9ccfd8)),
|
||||
(White, hex(0xe0def4)),
|
||||
];
|
||||
|
||||
for map in maps.iter() {
|
||||
color[map.0] = Some(map.1);
|
||||
}
|
||||
|
||||
let dupes = [
|
||||
(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 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);
|
||||
|
||||
self.cell_cache.clear();
|
||||
}
|
||||
|
||||
pub fn redraw(&mut self, term: &Term<TermListener>, context: &mut GraphicsContext<Window>) {
|
||||
let start = std::time::Instant::now();
|
||||
|
||||
let (width, height) = {
|
||||
let size = context.window().inner_size();
|
||||
(size.width as usize, size.height as usize)
|
||||
};
|
||||
|
||||
if width != self.width || height != self.height {
|
||||
self.resize(width, height);
|
||||
}
|
||||
|
||||
let cursor_color = Color::Named(NamedColor::Foreground);
|
||||
let cursor_color = self.color_to_u32(&cursor_color);
|
||||
let buffer = self.buffer.as_mut_slice();
|
||||
let content = term.renderable_content();
|
||||
|
||||
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;
|
||||
let term_coords = (term_row, term_col);
|
||||
|
||||
if (term_row + 1) * self.cell_height >= height
|
||||
|| (term_col + 1) * self.cell_width >= width
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if cell.flags.contains(CellFlags::HIDDEN) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let glyph_info = self.glyph_cache.cell_info(&cell);
|
||||
|
||||
if self.cell_cache.get(&term_coords) == Some(&glyph_info) {
|
||||
continue;
|
||||
} else {
|
||||
self.cell_cache.insert(term_coords, glyph_info.clone());
|
||||
}
|
||||
|
||||
let glyph = self.glyph_cache.lookup(&glyph_info);
|
||||
let px_row = term_row * self.cell_height * width;
|
||||
let mut px_start = px_row + term_col * self.cell_width;
|
||||
|
||||
for line in glyph.chunks_exact(self.cell_width) {
|
||||
let px_end = px_start + self.cell_width;
|
||||
|
||||
if px_end >= buffer.len() {
|
||||
println!("piss");
|
||||
break;
|
||||
}
|
||||
|
||||
buffer[px_start..px_end].copy_from_slice(line);
|
||||
px_start += width;
|
||||
}
|
||||
}
|
||||
|
||||
let term_row = content.cursor.point.line.0 as usize;
|
||||
let term_col = content.cursor.point.column.0 as usize;
|
||||
let term_coords = (term_row, term_col);
|
||||
|
||||
if (term_row + 1) * self.cell_height < height && (term_col + 1) * self.cell_width < width {
|
||||
let mut px_row = term_row * width * self.cell_height + term_col * self.cell_width;
|
||||
|
||||
use alacritty_terminal::ansi::CursorShape;
|
||||
match content.cursor.shape {
|
||||
CursorShape::Block => {
|
||||
for _y in 0..self.cell_height {
|
||||
for x in 0..self.cell_width {
|
||||
buffer[px_row + x as usize] = cursor_color;
|
||||
}
|
||||
|
||||
px_row += width;
|
||||
}
|
||||
}
|
||||
CursorShape::Beam => {
|
||||
for _y in 0..self.cell_height {
|
||||
buffer[px_row] = cursor_color;
|
||||
px_row += width;
|
||||
}
|
||||
}
|
||||
CursorShape::Underline => {
|
||||
px_row += (self.cell_height - 1) * width;
|
||||
for x in 0..self.cell_width {
|
||||
buffer[px_row + x as usize] = cursor_color;
|
||||
}
|
||||
}
|
||||
CursorShape::HollowBlock => {
|
||||
for x in 0..self.cell_width {
|
||||
buffer[px_row + x as usize] = cursor_color;
|
||||
}
|
||||
|
||||
for _y in 0..(self.cell_height - 1) {
|
||||
buffer[px_row] = cursor_color;
|
||||
buffer[px_row + self.cell_width - 1] = cursor_color;
|
||||
px_row += width;
|
||||
}
|
||||
|
||||
for x in 0..self.cell_width {
|
||||
buffer[px_row + x as usize] = cursor_color;
|
||||
}
|
||||
}
|
||||
CursorShape::Hidden => {}
|
||||
}
|
||||
|
||||
if content.cursor.shape != CursorShape::Hidden {
|
||||
self.cell_cache.remove(&term_coords);
|
||||
}
|
||||
}
|
||||
|
||||
let copy_start = std::time::Instant::now();
|
||||
context.set_buffer(&buffer, width as u16, height as u16);
|
||||
log::debug!("buffer copy time: {:?}", copy_start.elapsed());
|
||||
|
||||
log::debug!("redraw() time: {:?}", start.elapsed());
|
||||
}
|
||||
}
|
||||
|
||||
pub struct App {
|
||||
graphics: Graphics,
|
||||
graphics_context: GraphicsContext<Window>,
|
||||
|
@ -401,8 +55,8 @@ impl App {
|
|||
let graphics = Graphics::new();
|
||||
|
||||
let term_size = alacritty_terminal::term::SizeInfo::new(
|
||||
graphics.width as f32,
|
||||
graphics.height as f32,
|
||||
graphics.canvas.width as f32,
|
||||
graphics.canvas.height as f32,
|
||||
graphics.cell_width as f32,
|
||||
graphics.cell_height as f32,
|
||||
0.0,
|
||||
|
|
Loading…
Reference in New Issue