max / shop
1 file changed,
+30 insertions,
-0 deletions
| @@ -255,6 +255,10 @@ | |||
| 255 | 255 | // first use of a print run; any non-print Perform entry point (execute / | |
| 256 | 256 | // csi / esc / osc), newline, screen-swap, DECSTBM, or resize invalidates. | |
| 257 | 257 | cur_row_start: u32, | |
| 258 | + | // DECSET 2026: true between `\e[?2026h` and `\e[?2026l`. Purely | |
| 259 | + | // reflected via [`Grid::sync_update`] — the binary decides whether/how | |
| 260 | + | // long to defer redraws (typical timeout is ~150ms). | |
| 261 | + | sync_update: bool, | |
| 258 | 262 | pending_title: Option<String>, | |
| 259 | 263 | // Damage tracking — accumulated between take_damage() calls. | |
| 260 | 264 | row_dirty: Vec<bool>, | |
| @@ -294,6 +298,7 @@ | |||
| 294 | 298 | pending_fg_word: 0, | |
| 295 | 299 | pending_bg_word: 0, | |
| 296 | 300 | cur_row_start: CUR_ROW_INVALID, | |
| 301 | + | sync_update: false, | |
| 297 | 302 | pending_title: None, | |
| 298 | 303 | // Initial state: everything dirty so first render populates the | |
| 299 | 304 | // per-row cache. | |
| @@ -342,6 +347,13 @@ | |||
| 342 | 347 | self.cursor_shape | |
| 343 | 348 | } | |
| 344 | 349 | ||
| 350 | + | /// True while a DECSET 2026 synchronized-update batch is open. The | |
| 351 | + | /// renderer should hold off on presenting a frame until this clears | |
| 352 | + | /// (matching `\e[?2026l`) or its own safety timeout expires. | |
| 353 | + | pub fn sync_update(&self) -> bool { | |
| 354 | + | self.sync_update | |
| 355 | + | } | |
| 356 | + | ||
| 345 | 357 | /// Consume any window title set by the shell via OSC 0/2 since the last | |
| 346 | 358 | /// call. Binary polls after each `parser.advance` and forwards to | |
| 347 | 359 | /// `xdg_window.set_title`. | |
| @@ -1128,6 +1140,12 @@ | |||
| 1128 | 1140 | match code { | |
| 1129 | 1141 | 25 => self.cursor.visible = action == 'h', | |
| 1130 | 1142 | 1049 | 47 | 1047 => self.swap_alt(action == 'h'), | |
| 1143 | + | // DECSET/DECRST 2026: synchronized update. `h` | |
| 1144 | + | // begins a batch (renderer should hold frames | |
| 1145 | + | // until `l` or the caller's timeout); `l` ends | |
| 1146 | + | // it. Grid just tracks the state — the binary | |
| 1147 | + | // is what actually defers the redraw. | |
| 1148 | + | 2026 => self.sync_update = action == 'h', | |
| 1131 | 1149 | _ => {} | |
| 1132 | 1150 | } | |
| 1133 | 1151 | } | |
| @@ -1420,6 +1438,18 @@ | |||
| 1420 | 1438 | assert_eq!(g.cursor_shape(), CursorShape::Block); | |
| 1421 | 1439 | } | |
| 1422 | 1440 | ||
| 1441 | + | // ---- synchronized update (DECSET 2026) ----------------------------- | |
| 1442 | + | ||
| 1443 | + | #[test] | |
| 1444 | + | fn sync_update_toggles_on_2026() { | |
| 1445 | + | let mut g = Grid::new(10, 1); | |
| 1446 | + | assert!(!g.sync_update()); | |
| 1447 | + | feed(&mut g, b"\x1b[?2026h"); | |
| 1448 | + | assert!(g.sync_update()); | |
| 1449 | + | feed(&mut g, b"\x1b[?2026l"); | |
| 1450 | + | assert!(!g.sync_update()); | |
| 1451 | + | } | |
| 1452 | + | ||
| 1423 | 1453 | // ---- alt screen ---------------------------------------------------- | |
| 1424 | 1454 | ||
| 1425 | 1455 | #[test] |