Skip to content

Commit

Permalink
feat(lib): add methods to access terminal grid cells to Screen
Browse files Browse the repository at this point in the history
  • Loading branch information
tomcur committed Jun 30, 2024
1 parent f754d74 commit 361ae84
Show file tree
Hide file tree
Showing 2 changed files with 88 additions and 30 deletions.
18 changes: 10 additions & 8 deletions termsnap-lib/src/colors.rs
Original file line number Diff line number Diff line change
@@ -1,27 +1,29 @@
use alacritty_terminal::{
term::color::Colors as AlacrittyColors,
vte::ansi::{Color, NamedColor, Rgb},
vte::ansi::{Color, NamedColor, Rgb as AlacrittyRgb},
};

use std::collections::HashMap;

use crate::Screen;
use crate::{Rgb, Screen};

pub(crate) struct Colors {
colors: AlacrittyColors,
}

impl Colors {
pub fn to_rgb(&self, color: Color) -> Rgb {
match color {
let AlacrittyRgb { r, g, b } = match color {
Color::Named(named_color) => {
self.colors[named_color as usize].expect("all colors should be defined")
}
Color::Indexed(idx) => {
self.colors[usize::from(idx)].expect("all colors should be defined")
}
Color::Spec(rgb) => rgb,
}
};

Rgb { r, g, b }
}
}

Expand Down Expand Up @@ -81,7 +83,7 @@ fn fill_cube(colors: &mut AlacrittyColors) {
for g in 0..6 {
for b in 0..6 {
// Override colors 16..232 with the config (if present).
colors[index] = Some(Rgb {
colors[index] = Some(AlacrittyRgb {
r: if r == 0 { 0 } else { r * 40 + 55 },
g: if g == 0 { 0 } else { g * 40 + 55 },
b: if b == 0 { 0 } else { b * 40 + 55 },
Expand All @@ -101,7 +103,7 @@ fn fill_gray_ramp(colors: &mut AlacrittyColors) {
// Build colors.
for i in 0..24 {
let value = i * 10 + 8;
colors[index] = Some(Rgb {
colors[index] = Some(AlacrittyRgb {
r: value,
g: value,
b: value,
Expand All @@ -112,7 +114,7 @@ fn fill_gray_ramp(colors: &mut AlacrittyColors) {
debug_assert!(index == 256);
}

pub(crate) fn most_common_color(colors: &Colors, screen: &Screen) -> Rgb {
pub(crate) fn most_common_color(screen: &Screen) -> Rgb {
use std::hash::{Hash, Hasher};

#[derive(PartialEq, Eq, Copy, Clone)]
Expand Down Expand Up @@ -156,7 +158,7 @@ pub(crate) fn most_common_color(colors: &Colors, screen: &Screen) -> Rgb {
let cell = &screen.cells[usize::from(idx)];
let bg = &cell.bg;

*counts.entry(Rgb_(colors.to_rgb(*bg))).or_insert(0) += 1;
*counts.entry(Rgb_(*bg)).or_insert(0) += 1;
}

counts
Expand Down
100 changes: 78 additions & 22 deletions termsnap-lib/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,13 @@
#[forbid(unsafe_code)]

use std::fmt::{Display, Write};

use alacritty_terminal::{
term::{
cell::{Cell, Flags},
cell::{Cell as AlacrittyCell, Flags},
test::TermSize,
Config, Term as AlacrittyTerm,
},
vte::{
self,
ansi::{Processor, Rgb},
},
vte::{self, ansi::Processor},
};

mod colors;
Expand All @@ -20,6 +16,46 @@ use colors::Colors;
const FONT_ASPECT_RATIO: f32 = 0.6;
const FONT_ASCENT: f32 = 0.750;

/// A color in the sRGB color space.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Rgb {
pub r: u8,
pub g: u8,
pub b: u8,
}

impl Display for Rgb {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "#{:02x?}{:02x?}{:02x}", self.r, self.g, self.b)
}
}

/// The unicode character and style of a single cell in the terminal grid.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Cell {
pub c: char,
pub fg: Rgb,
pub bg: Rgb,
pub bold: bool,
pub italic: bool,
pub underline: bool,
pub strikethrough: bool,
}

impl Cell {
fn from_alacritty_cell(colors: &Colors, cell: &AlacrittyCell) -> Self {
Cell {
c: cell.c,
fg: colors.to_rgb(cell.fg),
bg: colors.to_rgb(cell.bg),
bold: cell.flags.intersects(Flags::BOLD),
italic: cell.flags.intersects(Flags::ITALIC),
underline: cell.flags.intersects(Flags::ALL_UNDERLINES),
strikethrough: cell.flags.intersects(Flags::STRIKEOUT),
}
}
}

#[derive(PartialEq)]
struct TextStyle {
fg: Rgb,
Expand All @@ -31,14 +67,22 @@ struct TextStyle {

impl TextStyle {
/// private conversion from alacritty Cell to Style
fn from_cell(colors: &Colors, cell: &Cell) -> Self {
TextStyle {
fg: colors.to_rgb(cell.fg),
fn from_cell(cell: &Cell) -> Self {
let Cell {
fg,
bold,
italic,
underline,
strikethrough,
..
} = *cell;

bold: cell.flags.intersects(Flags::BOLD),
italic: cell.flags.intersects(Flags::ITALIC),
underline: cell.flags.intersects(Flags::ALL_UNDERLINES),
strikethrough: cell.flags.intersects(Flags::STRIKEOUT),
TextStyle {
fg,
bold,
italic,
underline,
strikethrough,
}
}
}
Expand Down Expand Up @@ -218,9 +262,7 @@ impl Screen {
"#,
)?;

let colors = Colors::default();

let main_bg = colors::most_common_color(&colors, self.screen);
let main_bg = colors::most_common_color(self.screen);
fmt_rect(
f,
0,
Expand All @@ -241,7 +283,7 @@ impl Screen {
}

let cell = &cells[idx];
let bg = colors.to_rgb(cell.bg);
let bg = cell.bg;

if bg == main_bg {
continue;
Expand All @@ -253,7 +295,7 @@ impl Screen {
for x1 in x0 + 1..*columns {
let idx = self.screen.idx(y0, x1);
let cell = &cells[idx];
if colors.to_rgb(cell.bg) == bg {
if cell.bg == bg {
end_x = x1;
} else {
break;
Expand All @@ -265,7 +307,7 @@ impl Screen {
for x1 in x0 + 1..*columns {
let idx = self.screen.idx(y1, x1);
let cell = &cells[idx];
if colors.to_rgb(cell.bg) != bg {
if cell.bg != bg {
all = false;
break;
}
Expand Down Expand Up @@ -294,13 +336,13 @@ impl Screen {
for y in 0..*lines {
let idx = self.screen.idx(y, 0);
let cell = &cells[idx];
let mut style = TextStyle::from_cell(&colors, cell);
let mut style = TextStyle::from_cell(cell);
let mut start_x = 0;

for x in 0..*columns {
let idx = self.screen.idx(y, x);
let cell = &cells[idx];
let style_ = TextStyle::from_cell(&colors, cell);
let style_ = TextStyle::from_cell(cell);

if style_ != style {
if !text_line.is_empty() {
Expand Down Expand Up @@ -356,6 +398,17 @@ impl Screen {
pub fn columns(&self) -> u16 {
self.columns
}

/// An iterator over all cells in the terminal grid. This iterates over all columns in the
/// first line from left to right, then the second line, etc.
pub fn cells(&self) -> impl Iterator<Item = &Cell> {
self.cells.iter()
}

/// Get the cell at the terminal grid position specified by `line` and `column`.
pub fn get(&self, line: u16, column: u16) -> Option<&Cell> {
self.cells.get(self.idx(line, column))
}
}

/// A sink for responses sent by the [terminal emulator](Term). The terminal emulator sends
Expand Down Expand Up @@ -444,14 +497,17 @@ impl<W: PtyWriter> Term<W> {

/// Get a snapshot of the current terminal screen.
pub fn current_screen(&self) -> Screen {
// ideally users can define their own colors
let colors = Colors::default();

Screen {
lines: self.lines,
columns: self.columns,
cells: self
.term
.grid()
.display_iter()
.map(|point_cell| point_cell.cell.clone())
.map(|point_cell| Cell::from_alacritty_cell(&colors, point_cell.cell))
.collect(),
}
}
Expand Down

0 comments on commit 361ae84

Please sign in to comment.