little-kitty 0.0.3

A low-level interface for the Kitty Graphics Protocol
Documentation
use super::{super::utils::*, constants::*};

use std::{cmp::*, io::*};

type XY = (usize, usize);

//
// KittyPlaceholderWriter
//

/// Kitty graphics protocol placeholder writer utilities.
pub trait KittyPlaceholderWriter {
    /// Write a single placeholder cell.
    ///
    /// The ID range depends on the terminal color range, e.g. 8 bits for 256 color mode and 24
    /// bits for true color mode.
    ///
    /// Position is absolute (column, row). [None] implies the current cursor position. If position
    /// is [Some] will restore the current cursor position after writing.
    fn write_placeholder_cell(
        &mut self,
        id: usize,
        column: usize,
        row: usize,
        position: Option<XY>,
        background_color: Option<usize>,
    ) -> Result<()>;

    /// Write a row of placeholder cells.
    ///
    /// The ID range depends on the terminal color range, e.g. 8 bits for 256 color mode and 24
    /// bits for true color mode.
    ///
    /// Position is absolute (column, row). [None] implies the current cursor position. If position
    /// is [Some] will restore the current cursor position after writing.
    fn write_placeholder_row(
        &mut self,
        id: usize,
        start_column: usize,
        row: usize,
        columns: usize,
        position: Option<XY>,
        background_color: Option<usize>,
    ) -> Result<()>;

    /// Write a block of placeholder cells.
    ///
    /// The ID range depends on the terminal color range, e.g. 8 bits for 256 color mode and 24
    /// bits for true color mode.
    ///
    /// Position is absolute (column, row). [None] implies the current cursor position. If position
    /// is [Some] will restore the current cursor position after writing.
    fn write_placeholder_block(
        &mut self,
        id: usize,
        start_column: usize,
        start_row: usize,
        columns: usize,
        rows: usize,
        position: Option<XY>,
        background_color: Option<usize>,
    ) -> Result<()>;
}

impl<WriteT> KittyPlaceholderWriter for WriteT
where
    WriteT: Write,
{
    fn write_placeholder_cell(
        &mut self,
        id: usize,
        column: usize,
        row: usize,
        position: Option<XY>,
        background_color: Option<usize>,
    ) -> Result<()> {
        let writer = &mut PositionalWriter::new_save_at_maybe(self, position)?;
        write_start(writer, id, background_color)?;
        write_placeholder(writer, column, row)?;
        write_end(writer, background_color.is_some())
    }

    fn write_placeholder_row(
        &mut self,
        id: usize,
        start_column: usize,
        row: usize,
        columns: usize,
        position: Option<XY>,
        background_color: Option<usize>,
    ) -> Result<()> {
        let writer = &mut PositionalWriter::new_save_at_maybe(self, position)?;
        write_start(writer, id, background_color)?;
        for column in start_column..columns {
            write_placeholder(writer, column, row)?;
        }
        write_end(writer, background_color.is_some())
    }

    fn write_placeholder_block(
        &mut self,
        id: usize,
        start_column: usize,
        start_row: usize,
        columns: usize,
        rows: usize,
        position: Option<XY>,
        background_color: Option<usize>,
    ) -> Result<()> {
        let writer = &mut PositionalWriter::new_save_at_maybe(self, position)?;

        for row in start_row..rows {
            if let Some((x, y)) = position {
                writer.at(x as u16, (y + row) as u16)?;
            }

            // Write row
            write_start(writer, id, background_color)?;
            for column in start_column..columns {
                write_placeholder(writer, column, row)?;
            }
            write_end(writer, background_color.is_some())?;

            if position.is_none() {
                write!(writer, "\n")?;
            }
        }

        Ok(())
    }
}

// Utils

const VT100_SET_FOREGROUND_START: &[u8] = b"\x1b[38;5;";
const VT100_SET_BACKGROUND_START: &[u8] = b"\x1b[48;5;";
const VT100_END: &[u8] = b"m";
const VT100_RESET_FOREGROUND: &[u8] = b"\x1b[39m";
const VT100_RESET_BACKGROUND: &[u8] = b"\x1b[49m";

fn write_start<WriteT>(writer: &mut WriteT, id: usize, background_color: Option<usize>) -> Result<()>
where
    WriteT: Write,
{
    writer.write_all(VT100_SET_FOREGROUND_START)?;
    write!(writer, "{}", id)?;
    writer.write_all(VT100_END)?;

    if let Some(background_color) = background_color {
        writer.write_all(VT100_SET_BACKGROUND_START)?;
        write!(writer, "{}", background_color)?;
        writer.write_all(VT100_END)?;
    }

    Ok(())
}

fn write_end<WriteT>(writer: &mut WriteT, with_background_color: bool) -> Result<()>
where
    WriteT: Write,
{
    writer.write_all(VT100_RESET_FOREGROUND)?;
    if with_background_color {
        writer.write_all(VT100_RESET_BACKGROUND)?;
    }
    Ok(())
}

fn write_placeholder<WriteT>(writer: &mut WriteT, mut column: usize, mut row: usize) -> Result<()>
where
    WriteT: Write,
{
    column = min(column, PLACEHOLDER_DIACRITICS_LAST_INDEX);
    row = min(row, PLACEHOLDER_DIACRITICS_LAST_INDEX);
    write!(writer, "{}", PLACEHOLDER)?;
    write!(writer, "{}", PLACEHOLDER_DIACRITICS[row])?;
    write!(writer, "{}", PLACEHOLDER_DIACRITICS[column])
}