fix(compatibility): implement device reports (#500)
* fix(compatibility): implement device reports * docs(changelog): update change
This commit is contained in:
parent
a24c7f79f1
commit
b93e51cf88
8 changed files with 120 additions and 0 deletions
|
|
@ -12,6 +12,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
|
|||
* Fix propagation of plugin ui request (https://github.com/zellij-org/zellij/pull/495)
|
||||
* Handle pasted text properly (https://github.com/zellij-org/zellij/pull/494)
|
||||
* Fix default keybinds for tab -> resize mode (https://github.com/zellij-org/zellij/pull/497)
|
||||
* Terminal compatibility: device reports (https://github.com/zellij-org/zellij/pull/500)
|
||||
|
||||
## [0.9.0] - 2021-05-11
|
||||
* Add more functionality to unbinding the default keybindings (https://github.com/zellij-org/zellij/pull/468)
|
||||
|
|
|
|||
|
|
@ -9,7 +9,9 @@ use vte::{Params, Perform};
|
|||
const TABSTOP_WIDTH: usize = 8; // TODO: is this always right?
|
||||
const SCROLL_BACK: usize = 10_000;
|
||||
|
||||
use crate::utils::consts::VERSION;
|
||||
use crate::utils::logging::debug_log_to_file;
|
||||
use crate::utils::shared::version_number;
|
||||
|
||||
use crate::panes::terminal_character::{
|
||||
CharacterStyles, CharsetIndex, Cursor, CursorShape, StandardCharset, TerminalCharacter,
|
||||
|
|
@ -186,6 +188,7 @@ pub struct Grid {
|
|||
pub clear_viewport_before_rendering: bool,
|
||||
pub width: usize,
|
||||
pub height: usize,
|
||||
pub pending_messages_to_pty: Vec<Vec<u8>>,
|
||||
}
|
||||
|
||||
impl Debug for Grid {
|
||||
|
|
@ -222,6 +225,7 @@ impl Grid {
|
|||
alternative_lines_above_viewport_and_cursor: None,
|
||||
clear_viewport_before_rendering: false,
|
||||
active_charset: Default::default(),
|
||||
pending_messages_to_pty: vec![],
|
||||
}
|
||||
}
|
||||
pub fn contains_widechar(&self) -> bool {
|
||||
|
|
@ -1283,6 +1287,64 @@ impl Perform for Grid {
|
|||
for _ in 0..next_param_or(1) {
|
||||
self.move_to_previous_tabstop();
|
||||
}
|
||||
} else if c == 'c' {
|
||||
// identify terminal
|
||||
// https://vt100.net/docs/vt510-rm/DA1.html
|
||||
match intermediates.get(0) {
|
||||
None | Some(0) => {
|
||||
// primary device attributes
|
||||
let terminal_capabilities = "\u{1b}[?6c";
|
||||
self.pending_messages_to_pty
|
||||
.push(terminal_capabilities.as_bytes().to_vec());
|
||||
}
|
||||
Some(b'>') => {
|
||||
// secondary device attributes
|
||||
let version = version_number(VERSION);
|
||||
let text = format!("\u{1b}[>0;{};1c", version);
|
||||
self.pending_messages_to_pty.push(text.as_bytes().to_vec());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
} else if c == 'n' {
|
||||
// DSR - device status report
|
||||
// https://vt100.net/docs/vt510-rm/DSR.html
|
||||
match next_param_or(0) {
|
||||
5 => {
|
||||
// report terminal status
|
||||
let all_good = "\u{1b}[0n";
|
||||
self.pending_messages_to_pty
|
||||
.push(all_good.as_bytes().to_vec());
|
||||
}
|
||||
6 => {
|
||||
// CPR - cursor position report
|
||||
let position_report =
|
||||
format!("\x1b[{};{}R", self.cursor.y + 1, self.cursor.x + 1);
|
||||
self.pending_messages_to_pty
|
||||
.push(position_report.as_bytes().to_vec());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
} else if c == 't' {
|
||||
match next_param_or(1) as usize {
|
||||
14 => {
|
||||
// TODO: report text area size in pixels, currently unimplemented
|
||||
// to solve this we probably need to query the user's terminal for the cursor
|
||||
// size and then use it as a multiplier
|
||||
}
|
||||
18 => {
|
||||
// report text area
|
||||
let text_area_report = format!("\x1b[8;{};{}t", self.height, self.width);
|
||||
self.pending_messages_to_pty
|
||||
.push(text_area_report.as_bytes().to_vec());
|
||||
}
|
||||
22 => {
|
||||
// TODO: push title
|
||||
}
|
||||
23 => {
|
||||
// TODO: pop title
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
} else {
|
||||
let result = debug_log_to_file(format!("Unhandled csi: {}->{:?}", c, params));
|
||||
#[cfg(not(test))]
|
||||
|
|
@ -1341,6 +1403,11 @@ impl Perform for Grid {
|
|||
(b'7', None) => {
|
||||
self.save_cursor_position();
|
||||
}
|
||||
(b'Z', None) => {
|
||||
let terminal_capabilities = "\u{1b}[?6c";
|
||||
self.pending_messages_to_pty
|
||||
.push(terminal_capabilities.as_bytes().to_vec());
|
||||
}
|
||||
(b'8', None) => {
|
||||
self.restore_cursor_position();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -301,6 +301,9 @@ impl Pane for TerminalPane {
|
|||
CursorShape::BlinkingBeam => "\u{1b}[5 q".to_string(),
|
||||
}
|
||||
}
|
||||
fn drain_messages_to_pty(&mut self) -> Vec<Vec<u8>> {
|
||||
self.grid.pending_messages_to_pty.drain(..).collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl TerminalPane {
|
||||
|
|
|
|||
|
|
@ -383,3 +383,15 @@ fn csi_capital_z() {
|
|||
}
|
||||
assert_snapshot!(format!("{:?}", grid));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn terminal_reports() {
|
||||
let mut vte_parser = vte::Parser::new();
|
||||
let mut grid = Grid::new(51, 97);
|
||||
let fixture_name = "terminal_reports";
|
||||
let content = read_fixture(fixture_name);
|
||||
for byte in content {
|
||||
vte_parser.advance(&mut grid, byte);
|
||||
}
|
||||
assert_snapshot!(format!("{:?}", grid.pending_messages_to_pty));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
source: src/client/panes/./unit/grid_tests.rs
|
||||
expression: "format!(\"{:?}\", grid . pending_messages_to_pty)"
|
||||
|
||||
---
|
||||
[[27, 91, 63, 54, 99], [27, 91, 56, 59, 53, 49, 59, 57, 55, 116], [27, 91, 63, 54, 99], [27, 91, 48, 110], [27, 91, 49, 59, 49, 82]]
|
||||
|
|
@ -218,6 +218,11 @@ pub trait Pane {
|
|||
fn invisible_borders(&self) -> bool {
|
||||
false
|
||||
}
|
||||
fn drain_messages_to_pty(&mut self) -> Vec<Vec<u8>> {
|
||||
// TODO: this is only relevant to terminal panes
|
||||
// we should probably refactor away from this trait at some point
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
|
||||
impl Tab {
|
||||
|
|
@ -601,6 +606,10 @@ impl Tab {
|
|||
// the reason
|
||||
if let Some(terminal_output) = self.panes.get_mut(&PaneId::Terminal(pid)) {
|
||||
terminal_output.handle_pty_bytes(bytes);
|
||||
let messages_to_pty = terminal_output.drain_messages_to_pty();
|
||||
for message in messages_to_pty {
|
||||
self.write_to_pane_id(message, PaneId::Terminal(pid));
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn write_to_terminals_on_current_tab(&mut self, input_bytes: Vec<u8>) {
|
||||
|
|
|
|||
|
|
@ -85,3 +85,24 @@ pub fn _detect_theme(bg: PaletteColor) -> Theme {
|
|||
_ => Theme::Dark,
|
||||
}
|
||||
}
|
||||
|
||||
// (this was shamelessly copied from alacritty)
|
||||
//
|
||||
// This returns the current terminal version as a unique number based on the
|
||||
// semver version. The different versions are padded to ensure that a higher semver version will
|
||||
// always report a higher version number.
|
||||
pub fn version_number(mut version: &str) -> usize {
|
||||
if let Some(separator) = version.rfind('-') {
|
||||
version = &version[..separator];
|
||||
}
|
||||
|
||||
let mut version_number = 0;
|
||||
|
||||
let semver_versions = version.split('.');
|
||||
for (i, semver_version) in semver_versions.rev().enumerate() {
|
||||
let semver_number = semver_version.parse::<usize>().unwrap_or(0);
|
||||
version_number += usize::pow(100, i as u32) * semver_number;
|
||||
}
|
||||
|
||||
version_number
|
||||
}
|
||||
|
|
|
|||
1
src/tests/fixtures/terminal_reports
vendored
Normal file
1
src/tests/fixtures/terminal_reports
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
[0c[18tZ[5n[6n
|
||||
Loading…
Add table
Reference in a new issue