diff options
Diffstat (limited to 'alacritty_terminal/src/term/mod.rs')
-rw-r--r-- | alacritty_terminal/src/term/mod.rs | 110 |
1 files changed, 110 insertions, 0 deletions
diff --git a/alacritty_terminal/src/term/mod.rs b/alacritty_terminal/src/term/mod.rs index 1808f3aa..1e87ec8a 100644 --- a/alacritty_terminal/src/term/mod.rs +++ b/alacritty_terminal/src/term/mod.rs @@ -1,6 +1,7 @@ //! Exports the `Term` type which is a high-level API for the Grid. use std::cmp::{max, min}; +use std::fmt::Write; use std::ops::{Index, IndexMut, Range}; use std::sync::Arc; use std::{mem, ptr, str}; @@ -9,12 +10,16 @@ use bitflags::bitflags; use log::{debug, trace}; use serde::{Deserialize, Serialize}; use unicode_width::UnicodeWidthChar; +use vte::Params; use crate::ansi::{ self, Attr, CharsetIndex, Color, CursorShape, CursorStyle, Handler, NamedColor, StandardCharset, }; use crate::config::Config; use crate::event::{Event, EventListener}; +use crate::graphics::{ + sixel, GraphicCell, GraphicData, Graphics, TextureRef, UpdateQueues, MAX_GRAPHIC_DIMENSIONS, +}; use crate::grid::{Dimensions, Grid, GridIterator, Scroll}; use crate::index::{self, Boundary, Column, Direction, Line, Point, Side}; use crate::selection::{Selection, SelectionRange}; @@ -62,6 +67,8 @@ bitflags! { const ALTERNATE_SCROLL = 0b0000_1000_0000_0000_0000; const VI = 0b0001_0000_0000_0000_0000; const URGENCY_HINTS = 0b0010_0000_0000_0000_0000; + const SIXEL_SCROLLING = 0b0100_0000_0000_0000_0000; + const SIXEL_PRIV_PALETTE = 0b1000_0000_0000_0000_0000; const ANY = std::u32::MAX; } } @@ -72,6 +79,8 @@ impl Default for TermMode { | TermMode::LINE_WRAP | TermMode::ALTERNATE_SCROLL | TermMode::URGENCY_HINTS + | TermMode::SIXEL_SCROLLING + | TermMode::SIXEL_PRIV_PALETTE } } @@ -269,6 +278,9 @@ pub struct Term<T> { /// Information about cell dimensions. cell_width: usize, cell_height: usize, + + /// Data to add graphics to a grid. + graphics: Graphics, } impl<T> Term<T> { @@ -320,6 +332,7 @@ impl<T> Term<T> { selection: None, cell_width: size.cell_width as usize, cell_height: size.cell_height as usize, + graphics: Graphics::default(), } } @@ -470,6 +483,12 @@ impl<T> Term<T> { &mut self.grid } + /// Get queues to update graphic data. If both queues are empty, it returns + /// `None`. + pub fn graphics_take_queues(&mut self) -> Option<UpdateQueues> { + self.graphics.take_queues() + } + /// Resize terminal to new dimensions. pub fn resize(&mut self, size: SizeInfo) { self.cell_width = size.cell_width as usize; @@ -1578,6 +1597,10 @@ impl<T: EventListener> Handler for Term<T> { style.blinking = true; self.event_proxy.send_event(Event::CursorBlinkingChange(true)); }, + ansi::Mode::SixelScrolling => self.mode.insert(TermMode::SIXEL_SCROLLING), + ansi::Mode::SixelPrivateColorRegisters => { + self.mode.insert(TermMode::SIXEL_PRIV_PALETTE) + }, } } @@ -1620,6 +1643,11 @@ impl<T: EventListener> Handler for Term<T> { style.blinking = false; self.event_proxy.send_event(Event::CursorBlinkingChange(false)); }, + ansi::Mode::SixelScrolling => self.mode.remove(TermMode::SIXEL_SCROLLING), + ansi::Mode::SixelPrivateColorRegisters => { + self.graphics.sixel_shared_palette = None; + self.mode.remove(TermMode::SIXEL_PRIV_PALETTE); + }, } } @@ -1742,6 +1770,88 @@ impl<T: EventListener> Handler for Term<T> { let text = format!("\x1b[8;{};{}t", self.screen_lines(), self.columns()); self.event_proxy.send_event(Event::PtyWrite(text)); } + + fn start_sixel_graphic(&mut self, params: &Params) -> Option<Box<sixel::Parser>> { + let palette = self.graphics.sixel_shared_palette.take(); + Some(Box::new(sixel::Parser::new(params, palette))) + } + + fn insert_graphic(&mut self, graphic: GraphicData, palette: Option<Vec<Rgb>>) { + // Store last palette if we receive a new one, and it is shared. + if let Some(palette) = palette { + if !self.mode.contains(TermMode::SIXEL_PRIV_PALETTE) { + self.graphics.sixel_shared_palette = Some(palette); + } + } + + if graphic.width > MAX_GRAPHIC_DIMENSIONS.0 || graphic.height > MAX_GRAPHIC_DIMENSIONS.1 { + return; + } + + let width = graphic.width as u16; + let height = graphic.height as u16; + + if width == 0 || height == 0 { + return; + } + + // Add the graphic data to the pending queue. + let graphic_id = self.graphics.next_id(); + self.graphics.pending.push(GraphicData { id: graphic_id, ..graphic }); + + // If SIXEL_SCROLLING is enabled, the start of the graphic is the + // cursor position, and the grid can be scrolled if the graphic is + // larger than the screen. The cursor is moved to the next line + // after the graphic. + // + // If it is disabled, the graphic starts at (0, 0), the grid is never + // scrolled, and the cursor position is unmodified. + + let scrolling = self.mode.contains(TermMode::SIXEL_SCROLLING); + + // Fill the cells under the graphic. + // + // The cell in the first column contains a reference to the graphic, + // with the offset from the start. Rest of the cells are empty. + + let left = if scrolling { self.grid.cursor.point.column.0 } else { 0 }; + + let texture = Arc::new(TextureRef { + id: graphic_id, + remove_queue: Arc::downgrade(&self.graphics.remove_queue), + }); + + for (top, offset_y) in (0..).zip((0..height).step_by(self.cell_height)) { + let line = if scrolling { + self.grid.cursor.point.line + } else { + // Check if the image is beyond the screen limit. + if top >= self.screen_lines().0 { + break; + } + + Line(top) + }; + + // Store a reference to the graphic in the first column. + let graphic_cell = GraphicCell { texture: texture.clone(), offset_x: 0, offset_y }; + let mut cell = Cell::default(); + cell.set_graphic(graphic_cell); + self.grid[line][Column(left)] = cell; + + for col in left + 1..self.cols().0 { + self.grid[line][Column(col)] = Cell::default(); + } + + if scrolling { + self.linefeed(); + } + } + + if scrolling { + self.carriage_return(); + } + } } /// Terminal version for escape sequence reports. |