-
-
Notifications
You must be signed in to change notification settings - Fork 644
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(signals): support XTWINOPS 14 and 16 (and query the terminal for…
… them on startup and SIGWINCH) (#1316) * feat(signals): get pixel info from terminal emulator * feat(signals): query for pixel info on sigwinch * feat(signals): reply to csi 14t and csi 16t * style(fmt): rustfmt * style(comments): remove outdated
- Loading branch information
Showing
24 changed files
with
1,099 additions
and
27 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
[14t;[16t |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
use zellij_utils::pane_size::SizeInPixels; | ||
|
||
use zellij_utils::{ipc::PixelDimensions, lazy_static::lazy_static, regex::Regex}; | ||
|
||
use zellij_tile::data::Key; | ||
|
||
pub struct PixelCsiParser { | ||
expected_pixel_csi_instructions: usize, | ||
current_buffer: Vec<(Key, Vec<u8>)>, | ||
} | ||
|
||
impl PixelCsiParser { | ||
pub fn new() -> Self { | ||
PixelCsiParser { | ||
expected_pixel_csi_instructions: 0, | ||
current_buffer: vec![], | ||
} | ||
} | ||
pub fn increment_expected_csi_instructions(&mut self, by: usize) { | ||
self.expected_pixel_csi_instructions += by; | ||
} | ||
pub fn decrement_expected_csi_instructions(&mut self, by: usize) { | ||
self.expected_pixel_csi_instructions = | ||
self.expected_pixel_csi_instructions.saturating_sub(by); | ||
} | ||
pub fn expected_instructions(&self) -> usize { | ||
self.expected_pixel_csi_instructions | ||
} | ||
pub fn parse(&mut self, key: Key, raw_bytes: Vec<u8>) -> Option<PixelDimensionsOrKeys> { | ||
if let Key::Char('t') = key { | ||
self.current_buffer.push((key, raw_bytes)); | ||
match PixelDimensionsOrKeys::pixel_dimensions_from_keys(&self.current_buffer) { | ||
Ok(pixel_instruction) => { | ||
self.decrement_expected_csi_instructions(1); | ||
self.current_buffer.clear(); | ||
Some(pixel_instruction) | ||
} | ||
Err(_) => { | ||
self.expected_pixel_csi_instructions = 0; | ||
Some(PixelDimensionsOrKeys::Keys( | ||
self.current_buffer.drain(..).collect(), | ||
)) | ||
} | ||
} | ||
} else if self.key_is_valid(key) { | ||
self.current_buffer.push((key, raw_bytes)); | ||
None | ||
} else { | ||
self.current_buffer.push((key, raw_bytes)); | ||
self.expected_pixel_csi_instructions = 0; | ||
Some(PixelDimensionsOrKeys::Keys( | ||
self.current_buffer.drain(..).collect(), | ||
)) | ||
} | ||
} | ||
fn key_is_valid(&self, key: Key) -> bool { | ||
match key { | ||
Key::Esc => { | ||
// this is a UX improvement | ||
// in case the user's terminal doesn't support one or more of these signals, | ||
// if they spam ESC they need to be able to get back to normal mode and not "us | ||
// waiting for pixel instructions" mode | ||
if self | ||
.current_buffer | ||
.iter() | ||
.find(|(key, _)| *key == Key::Esc) | ||
.is_none() | ||
{ | ||
true | ||
} else { | ||
false | ||
} | ||
} | ||
Key::Char(';') | Key::Char('[') => true, | ||
Key::Char(c) => { | ||
if let '0'..='9' = c { | ||
true | ||
} else { | ||
false | ||
} | ||
} | ||
_ => false, | ||
} | ||
} | ||
} | ||
|
||
#[derive(Debug)] | ||
pub enum PixelDimensionsOrKeys { | ||
// TODO: rename to PixelDimensionsOrKeys | ||
PixelDimensions(PixelDimensions), | ||
Keys(Vec<(Key, Vec<u8>)>), | ||
} | ||
|
||
impl PixelDimensionsOrKeys { | ||
pub fn pixel_dimensions_from_keys(keys: &Vec<(Key, Vec<u8>)>) -> Result<Self, &'static str> { | ||
lazy_static! { | ||
static ref RE: Regex = Regex::new(r"^\u{1b}\[(\d+);(\d+);(\d+)t$").unwrap(); | ||
} | ||
let key_sequence: Vec<Option<char>> = keys | ||
.iter() | ||
.map(|(key, _)| match key { | ||
Key::Char(c) => Some(*c), | ||
Key::Esc => Some('\u{1b}'), | ||
_ => None, | ||
}) | ||
.collect(); | ||
if key_sequence.iter().all(|k| k.is_some()) { | ||
let key_string: String = key_sequence.iter().map(|k| k.unwrap()).collect(); | ||
let captures = RE | ||
.captures_iter(&key_string) | ||
.next() | ||
.ok_or("invalid_instruction")?; | ||
let csi_index = captures[1].parse::<usize>(); | ||
let first_field = captures[2].parse::<usize>(); | ||
let second_field = captures[3].parse::<usize>(); | ||
if csi_index.is_err() || first_field.is_err() || second_field.is_err() { | ||
return Err("invalid_instruction"); | ||
} | ||
match csi_index { | ||
Ok(4) => { | ||
// text area size | ||
Ok(PixelDimensionsOrKeys::PixelDimensions(PixelDimensions { | ||
character_cell_size: None, | ||
text_area_size: Some(SizeInPixels { | ||
height: first_field.unwrap(), | ||
width: second_field.unwrap(), | ||
}), | ||
})) | ||
} | ||
Ok(6) => { | ||
// character cell size | ||
Ok(PixelDimensionsOrKeys::PixelDimensions(PixelDimensions { | ||
character_cell_size: Some(SizeInPixels { | ||
height: first_field.unwrap(), | ||
width: second_field.unwrap(), | ||
}), | ||
text_area_size: None, | ||
})) | ||
} | ||
_ => Err("invalid sequence"), | ||
} | ||
} else { | ||
Err("invalid sequence") | ||
} | ||
} | ||
} |
Oops, something went wrong.