wip: in-progress changes (grid, config, wizard, settings, pty) before session-persistence
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
use alacritty_terminal::event::VoidListener;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use alacritty_terminal::event::{Event, EventListener};
|
||||
use alacritty_terminal::grid::Dimensions;
|
||||
use alacritty_terminal::index::{Column, Line, Point};
|
||||
use alacritty_terminal::term::{Config, Term};
|
||||
@@ -23,24 +25,55 @@ impl Dimensions for GridSize {
|
||||
}
|
||||
}
|
||||
|
||||
/// Collects the escape sequences the terminal model wants written back to the PTY
|
||||
/// (Primary/Secondary Device Attributes, DSR cursor/status reports, etc.). Programs
|
||||
/// like fish block on these replies at startup; with a void listener they hang ~2s
|
||||
/// and then warn ("could not read response to Primary Device Attribute query").
|
||||
#[derive(Clone, Default)]
|
||||
pub struct ReplyCollector {
|
||||
buf: Arc<Mutex<Vec<u8>>>,
|
||||
}
|
||||
|
||||
impl EventListener for ReplyCollector {
|
||||
fn send_event(&self, event: Event) {
|
||||
if let Event::PtyWrite(text) = event {
|
||||
if let Ok(mut b) = self.buf.lock() {
|
||||
b.extend_from_slice(text.as_bytes());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Owns an alacritty terminal model and feeds raw PTY bytes into it.
|
||||
pub struct GridSurface {
|
||||
term: Term<VoidListener>,
|
||||
term: Term<ReplyCollector>,
|
||||
parser: Processor,
|
||||
size: GridSize,
|
||||
replies: ReplyCollector,
|
||||
}
|
||||
|
||||
impl GridSurface {
|
||||
pub fn new(cols: u16, rows: u16) -> Self {
|
||||
let size = GridSize { cols: cols as usize, lines: rows as usize };
|
||||
let term = Term::new(Config::default(), &size, VoidListener);
|
||||
Self { term, parser: Processor::new(), size }
|
||||
let replies = ReplyCollector::default();
|
||||
let term = Term::new(Config::default(), &size, replies.clone());
|
||||
Self { term, parser: Processor::new(), size, replies }
|
||||
}
|
||||
|
||||
pub fn feed(&mut self, bytes: &[u8]) {
|
||||
self.parser.advance(&mut self.term, bytes);
|
||||
}
|
||||
|
||||
/// Drain any escape sequences the model produced in response to queries fed so
|
||||
/// far. The caller must write these back to the PTY for query-driven programs
|
||||
/// (fish, vim, etc.) to proceed without timing out.
|
||||
pub fn take_replies(&mut self) -> Vec<u8> {
|
||||
match self.replies.buf.lock() {
|
||||
Ok(mut b) => std::mem::take(&mut *b),
|
||||
Err(_) => Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resize(&mut self, cols: u16, rows: u16) {
|
||||
self.size = GridSize { cols: cols as usize, lines: rows as usize };
|
||||
self.term.resize(self.size);
|
||||
@@ -56,7 +89,7 @@ impl GridSurface {
|
||||
self.term.grid()[point].c
|
||||
}
|
||||
|
||||
pub fn term(&self) -> &Term<VoidListener> {
|
||||
pub fn term(&self) -> &Term<ReplyCollector> {
|
||||
&self.term
|
||||
}
|
||||
|
||||
@@ -97,4 +130,15 @@ mod tests {
|
||||
assert_eq!(g.char_at(0, 0), 'a');
|
||||
assert_eq!(g.char_at(1, 0), 'c');
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn primary_device_attribute_query_gets_a_reply() {
|
||||
// fish (and friends) send ESC[c at startup and block on the response.
|
||||
let mut g = GridSurface::new(20, 5);
|
||||
g.feed(b"\x1b[c");
|
||||
let reply = g.take_replies();
|
||||
assert!(reply.starts_with(b"\x1b[?"), "expected a DA1 reply, got {reply:?}");
|
||||
// Replies are drained, not duplicated.
|
||||
assert!(g.take_replies().is_empty());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user