1use crate::grid::{Grid, GridCell, GridCellFlags, GridLineFlags};
2use crate::input::mode;
3use crate::input::{CellState, InputEndType, ScreenWriter, COLOUR_DEFAULT};
4use crate::{TerminalPaletteIndex, TerminalPassthrough};
5
6use super::{SavedGrid, Screen, TITLE_STACK_MAX};
7
8fn cursor_backward_tab_steps(n: u32, current: u32) -> u32 {
9 n.max(1).min(current.saturating_add(1))
10}
11
12impl ScreenWriter for Screen {
13 fn collect_add(&mut self, ch: char, cell: &CellState) {
14 self.write_char(ch, cell, false);
15 }
16
17 fn collect_add_with_charset(&mut self, ch: char, cell: &CellState, acs: bool) {
18 self.write_char(ch, cell, acs);
19 }
20
21 fn collect_add_ascii_run(&mut self, bytes: &[u8], cell: &CellState, acs: bool) {
22 if !self.write_plain_ascii_run(bytes, cell, acs) {
23 for &byte in bytes {
24 self.write_char(char::from(byte), cell, acs);
25 }
26 }
27 }
28
29 fn cursor_up(&mut self, n: u32) {
30 self.clear_pending_wrap();
31 let minimum_y = if self.cursor_y >= self.rupper {
34 self.rupper
35 } else {
36 0
37 };
38 self.cursor_y = self.cursor_y.saturating_sub(n).max(minimum_y);
39 }
40
41 fn cursor_down(&mut self, n: u32) {
42 self.clear_pending_wrap();
43 let maximum_y = if self.cursor_y <= self.rlower {
46 self.rlower
47 } else {
48 self.grid.sy().saturating_sub(1)
49 };
50 self.cursor_y = self.cursor_y.saturating_add(n).min(maximum_y);
51 }
52
53 fn cursor_left(&mut self, n: u32) {
54 self.clear_pending_wrap();
55 self.cursor_x = self.cursor_column().saturating_sub(n);
56 }
57
58 fn cursor_right(&mut self, n: u32) {
59 self.clear_pending_wrap();
60 self.cursor_x = self
61 .cursor_column()
62 .saturating_add(n)
63 .min(self.max_cursor_x());
64 }
65
66 fn cursor_move(&mut self, col: i32, row: i32, origin_mode: bool) {
67 self.clear_pending_wrap();
68 let max_x = self.grid.sx().saturating_sub(1);
69 let (min_y, max_y) = if origin_mode && (self.mode & mode::MODE_ORIGIN) != 0 {
70 (self.rupper, self.rlower)
71 } else {
72 (0, self.grid.sy().saturating_sub(1))
73 };
74
75 if col >= 0 {
76 self.cursor_x = (col as u32).min(max_x);
77 }
78 if row >= 0 {
79 self.cursor_y = min_y.saturating_add(row as u32).min(max_y);
80 }
81 }
82
83 fn insert_line(&mut self, n: u32, bg: i32) {
84 self.clear_pending_wrap();
85 if self.cursor_y < self.rupper || self.cursor_y > self.rlower {
86 return;
87 }
88 self.clear_selected_cells();
89
90 let upper = self.cursor_y;
91 let lower = self.rlower;
92 let lines = n.max(1).min(lower.saturating_sub(upper).saturating_add(1));
93 for _ in 0..lines {
94 self.grid.scroll_region_down(upper, lower, bg);
95 }
96 }
97
98 fn delete_line(&mut self, n: u32, bg: i32) {
99 self.clear_pending_wrap();
100 if self.cursor_y < self.rupper || self.cursor_y > self.rlower {
101 return;
102 }
103 self.clear_selected_cells();
104
105 let upper = self.cursor_y;
106 let lower = self.rlower;
107 let lines = n.max(1).min(lower.saturating_sub(upper).saturating_add(1));
108 for _ in 0..lines {
109 self.grid.scroll_region_up(upper, lower, bg, false);
110 }
111 }
112
113 fn scroll_up(&mut self, n: u32, bg: i32) {
114 self.clear_pending_wrap();
115 self.clear_selected_cells();
116 let lines = n
117 .max(1)
118 .min(self.rlower.saturating_sub(self.rupper).saturating_add(1));
119 for _ in 0..lines {
120 self.grid
121 .scroll_region_up(self.rupper, self.rlower, bg, self.rupper == 0);
122 }
123 }
124
125 fn scroll_down(&mut self, n: u32, bg: i32) {
126 self.clear_pending_wrap();
127 self.clear_selected_cells();
128 let lines = n
129 .max(1)
130 .min(self.rlower.saturating_sub(self.rupper).saturating_add(1));
131 for _ in 0..lines {
132 self.grid.scroll_region_down(self.rupper, self.rlower, bg);
133 }
134 }
135
136 fn linefeed(&mut self, wrapped: bool, bg: i32) {
137 self.pending_wrap = false;
138 if wrapped {
139 if let Some(line) = self.current_line_mut() {
140 line.set_wrapped(true);
141 }
142 }
143
144 if self.cursor_y == self.rlower {
145 self.clear_selected_cells();
146 self.grid
147 .scroll_region_up(self.rupper, self.rlower, bg, self.rupper == 0);
148 } else if self.cursor_y < self.grid.sy().saturating_sub(1) {
149 self.cursor_y += 1;
150 }
151 }
152
153 fn reverse_index(&mut self, bg: i32) {
154 self.clear_pending_wrap();
155 if self.cursor_y == self.rupper {
156 self.clear_selected_cells();
157 self.grid.scroll_region_down(self.rupper, self.rlower, bg);
158 } else if self.cursor_y > 0 {
159 self.cursor_y -= 1;
160 }
161 }
162
163 fn carriage_return(&mut self) {
164 self.pending_wrap = false;
165 self.cursor_x = 0;
166 }
167
168 fn backspace(&mut self) {
169 self.clear_pending_wrap();
170 let cx = self.cursor_column();
171 if cx > 0 {
172 self.cursor_x = cx - 1;
173 return;
174 }
175 if self.cursor_y == 0 {
176 return;
177 }
178 if self
179 .grid
180 .visible_line(self.cursor_y - 1)
181 .is_some_and(|line| line.flags().contains(GridLineFlags::WRAPPED))
182 {
183 self.cursor_y -= 1;
184 self.cursor_x = self.max_cursor_x();
185 }
186 }
187
188 fn insert_character(&mut self, n: u32, bg: i32) {
189 self.clear_pending_wrap();
190 self.clear_selected_cells();
191 let sx = self.grid.sx();
192 let x = self.logical_insert_column();
193 if x >= sx {
194 self.pending_wrap = (self.mode & crate::input::mode::MODE_WRAP) != 0;
195 return;
196 }
197 self.cursor_x = x;
198 let count = n.max(1).min(sx.saturating_sub(x));
199 let blank = self.blank_cell(bg);
200 if let Some(line) = self.current_line_mut() {
201 line.insert_cells(x, count, &blank);
202 Self::repair_wide_cells_on_line(line, sx, bg);
203 line.touch();
204 }
205 }
206
207 fn delete_character(&mut self, n: u32, bg: i32) {
208 self.clear_pending_wrap();
209 self.clear_selected_cells();
210 let x = self.cursor_column();
211 let sx = self.grid.sx();
212 let count = n.max(1).min(sx.saturating_sub(x));
213 let blank = self.blank_cell(bg);
214 if let Some(line) = self.current_line_mut() {
215 line.delete_cells(x, count, &blank);
216 Self::repair_wide_cells_on_line(line, sx, bg);
217 line.touch();
218 }
219 }
220
221 fn clear_character(&mut self, n: u32, bg: i32) {
222 self.clear_pending_wrap();
223 let x = self.cursor_column();
224 let end = x
225 .saturating_add(n.max(1))
226 .saturating_sub(1)
227 .min(self.grid.sx().saturating_sub(1));
228 self.clear_line_range(self.cursor_y, x, end, bg);
229 }
230
231 fn clear_end_of_screen(&mut self, bg: i32) {
232 if self.cursor_y == 0 && self.cursor_column() == 0 {
233 self.clear_selected_cells();
234 self.grid.clear_visible_to_history(COLOUR_DEFAULT);
235 return;
236 }
237 let x = self.cursor_column();
238 if self.cursor_y < self.grid.sy() {
239 self.clear_line_range(self.cursor_y, x, self.grid.sx().saturating_sub(1), bg);
240 }
241 if self.cursor_y + 1 < self.grid.sy() {
242 self.clear_screen_region(
243 self.cursor_y + 1,
244 self.grid.sy().saturating_sub(1),
245 COLOUR_DEFAULT,
246 );
247 }
248 }
249
250 fn clear_start_of_screen(&mut self, bg: i32) {
251 if self.cursor_y > 0 {
252 self.clear_screen_region(0, self.cursor_y - 1, COLOUR_DEFAULT);
253 }
254 self.clear_line_range(self.cursor_y, 0, self.cursor_column(), bg);
255 }
256
257 fn clear_screen(&mut self, _bg: i32) {
258 self.clear_selected_cells();
259 self.grid.clear_visible_to_history(COLOUR_DEFAULT);
260 }
261
262 fn clear_history(&mut self) {
263 self.clear_selected_cells();
264 self.grid.clear_history();
265 }
266
267 fn clear_end_of_line(&mut self, bg: i32) {
268 self.clear_line_range(
269 self.cursor_y,
270 self.cursor_column(),
271 self.grid.sx().saturating_sub(1),
272 bg,
273 );
274 }
275
276 fn clear_start_of_line(&mut self, bg: i32) {
277 self.clear_line_range(self.cursor_y, 0, self.cursor_column(), bg);
278 }
279
280 fn clear_line(&mut self, bg: i32) {
281 self.clear_line_range(self.cursor_y, 0, self.grid.sx().saturating_sub(1), bg);
282 }
283
284 fn mode_set(&mut self, mode_bits: u32) {
285 self.mode |= mode_bits;
286 }
287
288 fn mode_clear(&mut self, mode_bits: u32) {
289 self.mode &= !mode_bits;
290 if (self.mode & mode::MODE_WRAP) == 0 {
291 self.clear_pending_wrap();
292 }
293 }
294
295 fn set_scroll_region(&mut self, top: u32, bottom: u32) {
296 let max_y = self.grid.sy().saturating_sub(1);
297 let top = top.min(max_y);
298 let bottom = bottom.min(max_y);
299 if top >= bottom {
300 return;
301 }
302 self.pending_wrap = false;
303 self.cursor_x = 0;
304 self.cursor_y = 0;
305 self.rupper = top;
306 self.rlower = bottom;
307 }
308
309 fn alternate_on(&mut self, bg: i32, save_cursor: bool) {
310 if !self.alternate_screen_enabled {
311 return;
312 }
313 if self.is_alternate() {
314 return;
315 }
316 self.clear_selected_cells();
317
318 let mut saved_grid = Grid::new(self.grid.size(), 0);
319 saved_grid.replace_visible(self.grid.visible_lines());
320 self.saved_grid = Some(SavedGrid {
321 grid: saved_grid,
322 history_enabled: self.grid.history_enabled(),
323 });
324 if save_cursor {
325 self.saved_cursor_x = Some(self.cursor_x);
326 self.saved_cursor_y = Some(self.cursor_y);
327 self.saved_cursor_pending_wrap = self.pending_wrap;
328 self.saved_state.cx = self.cursor_column();
329 self.saved_state.cy = self.cursor_y;
330 }
331
332 self.grid.clear_visible(bg);
333 self.grid.set_history_enabled(false);
334 self.pending_wrap = false;
335 if !save_cursor || !self.preserve_alternate_screen_cursor {
336 self.cursor_x = 0;
337 self.cursor_y = 0;
338 }
339 }
340
341 fn alternate_off(&mut self, _bg: i32, restore_cursor: bool) {
342 self.clear_selected_cells();
343 let saved_cursor = if restore_cursor {
344 self.saved_cursor_x
345 .zip(self.saved_cursor_y)
346 .map(|(x, y)| (x, y, self.saved_cursor_pending_wrap))
347 } else {
348 None
349 };
350
351 let Some(saved) = self.saved_grid.take() else {
352 if let Some((x, y, pending_wrap)) = saved_cursor {
353 self.restore_cursor_position(x, y, pending_wrap);
354 }
355 return;
356 };
357
358 let current_size = self.grid.size();
359 let saved_size = saved.grid.size();
360 let alternate_cursor = (self.cursor_x, self.cursor_y, self.pending_wrap);
361 self.grid
362 .restore_visible_at_size(saved_size, saved.grid.visible_lines(), COLOUR_DEFAULT);
363 self.grid.set_history_enabled(saved.history_enabled);
364 if let Some((x, y, pending_wrap)) = saved_cursor {
365 self.restore_cursor_position(x, y, pending_wrap);
366 } else {
367 self.restore_cursor_position(
368 alternate_cursor.0,
369 alternate_cursor.1,
370 alternate_cursor.2,
371 );
372 }
373 self.resize(current_size);
374 }
375
376 fn tab(&mut self) {
377 self.clear_pending_wrap();
378 let start = self.cursor_column();
379 let next = ((start + 1) as usize..self.tabs.len())
380 .find(|index| self.tabs[*index])
381 .map(|index| index as u32)
382 .unwrap_or_else(|| self.grid.sx().saturating_sub(1));
383 self.cursor_x = next;
384 }
385
386 fn cursor_backward_tab(&mut self, n: u32) {
387 self.clear_pending_wrap();
388 let mut current = self.cursor_column();
389 for _ in 0..cursor_backward_tab_steps(n, current) {
390 if current == 0 {
391 break;
392 }
393 let previous = (0..current as usize)
394 .rev()
395 .find(|index| self.tabs[*index])
396 .map(|index| index as u32)
397 .unwrap_or(0);
398 current = previous;
399 }
400 self.cursor_x = current;
401 }
402
403 fn set_tab_stop(&mut self) {
404 let column = self.cursor_column() as usize;
405 if let Some(tab) = self.tabs.get_mut(column) {
406 *tab = true;
407 }
408 }
409
410 fn clear_tab_stop(&mut self) {
411 let column = self.cursor_column() as usize;
412 if let Some(tab) = self.tabs.get_mut(column) {
413 *tab = false;
414 }
415 }
416
417 fn clear_all_tab_stops(&mut self) {
418 self.tabs.fill(false);
419 }
420
421 fn set_title(&mut self, title: &str) {
422 if self.title_rename_enabled {
423 Screen::set_title(self, title);
424 }
425 }
426
427 fn set_window_name(&mut self, name: &str) {
428 if self.title_rename_enabled {
429 self.window_name = name.to_owned();
430 }
431 }
432
433 fn set_path(&mut self, path: &str) {
434 self.path = path.to_owned();
435 }
436
437 fn save_cursor(&mut self) {
438 self.saved_cursor_x = Some(self.cursor_x);
439 self.saved_cursor_y = Some(self.cursor_y);
440 self.saved_cursor_pending_wrap = self.pending_wrap;
441 }
442
443 fn restore_cursor(&mut self) {
444 if let (Some(x), Some(y)) = (self.saved_cursor_x, self.saved_cursor_y) {
445 self.restore_cursor_position(x, y, self.saved_cursor_pending_wrap);
446 }
447 }
448
449 fn alignment_test(&mut self) {
450 self.clear_selected_cells();
451 self.rupper = 0;
452 self.rlower = self.grid.sy().saturating_sub(1);
453 let sx = self.grid.sx();
454 for y in 0..self.grid.sy() {
455 if let Some(line) = self.grid.visible_line_mut(y) {
456 for x in 0..sx {
457 if let Some(cell) = line.cell_mut(x) {
458 *cell = GridCell::from_state(
459 'E',
460 1,
461 &CellState::default(),
462 GridCellFlags::default(),
463 );
464 }
465 }
466 line.set_wrapped(false);
467 line.touch();
468 }
469 }
470 }
471
472 fn full_reset(&mut self) {
473 self.clear_selected_cells();
474 if self.is_alternate() {
475 self.alternate_off(COLOUR_DEFAULT, false);
476 }
477 self.cursor_x = 0;
478 self.cursor_y = 0;
479 self.pending_wrap = false;
480 self.rupper = 0;
481 self.rlower = self.grid.sy().saturating_sub(1);
482 self.mode = mode::MODE_CURSOR | mode::MODE_WRAP | (self.mode & mode::MODE_CRLF);
483 self.grid.clear_visible(COLOUR_DEFAULT);
484 self.reset_tabs();
485 self.title_stack.clear();
486 self.active_hyperlink = 0;
487 self.hyperlinks.reset();
488 }
489
490 fn start_sync(&mut self) {
491 self.mode |= mode::MODE_SYNC;
492 }
493
494 fn stop_sync(&mut self) {
495 self.mode &= !mode::MODE_SYNC;
496 }
497
498 fn set_cursor_style(&mut self, n: u32) {
499 self.cursor_style = n;
500 }
501
502 fn osc_hyperlink(&mut self, data: &str) {
503 let (internal_id, uri) = Self::parse_hyperlink(data);
504 if uri.is_empty() {
505 self.active_hyperlink = 0;
506 return;
507 }
508 self.active_hyperlink = self.hyperlinks.put(&uri, internal_id.as_deref());
509 }
510
511 fn current_hyperlink_id(&self) -> u32 {
512 self.active_hyperlink
513 }
514
515 fn bell(&mut self) {
516 self.bell_count = self.bell_count.saturating_add(1);
517 }
518
519 fn apc_passthrough(&mut self, data: &[u8]) {
520 self.push_terminal_passthrough(TerminalPassthrough::kitty_graphics(
521 self.cursor_x,
522 self.cursor_y,
523 data.to_vec(),
524 ));
525 }
526
527 fn dcs_passthrough(&mut self, data: &[u8]) {
528 self.push_terminal_passthrough(TerminalPassthrough::raw(
529 self.cursor_x,
530 self.cursor_y,
531 data.to_vec(),
532 ));
533 }
534
535 fn sixel_passthrough(&mut self, data: &[u8]) {
536 self.push_terminal_passthrough(TerminalPassthrough::sixel(
537 self.cursor_x,
538 self.cursor_y,
539 data.to_vec(),
540 ));
541 }
542
543 fn screen_size_x(&self) -> u32 {
544 self.grid.sx()
545 }
546
547 fn screen_size_y(&self) -> u32 {
548 self.grid.sy()
549 }
550
551 fn cursor_x(&self) -> u32 {
552 self.cursor_x
553 }
554
555 fn cursor_y(&self) -> u32 {
556 self.cursor_y
557 }
558
559 fn current_mode(&self) -> u32 {
560 self.mode
561 }
562
563 fn push_title(&mut self) {
564 if !self.title_rename_enabled {
565 return;
566 }
567 if self.title_stack.len() >= TITLE_STACK_MAX {
568 let excess = self.title_stack.len() + 1 - TITLE_STACK_MAX;
569 self.title_stack.drain(0..excess);
570 }
571 self.title_stack.push(self.title.clone());
572 }
573
574 fn pop_title(&mut self) {
575 if !self.title_rename_enabled {
576 return;
577 }
578 if let Some(title) = self.title_stack.pop() {
579 self.title = title;
580 }
581 }
582
583 fn osc_palette(&mut self, data: &str, _end: InputEndType) {
584 let mut fields = data.split(';');
591 while let Some(index) = fields.next() {
592 let Some(value) = fields.next() else {
593 break;
594 };
595 if value != "?" {
596 continue;
597 }
598 let Some(index) = TerminalPaletteIndex::parse(index) else {
599 continue;
600 };
601 self.push_terminal_passthrough(TerminalPassthrough::palette_query(index));
602 }
603 }
604 fn osc_notification(&mut self, _data: &str) {}
605 fn osc_fg_colour(&mut self, _data: &str, _end: InputEndType) {}
606 fn osc_bg_colour(&mut self, _data: &str, _end: InputEndType) {}
607 fn osc_cursor_colour(&mut self, _data: &str, _end: InputEndType) {}
608 fn osc_clipboard(&mut self, data: &str, end: InputEndType) {
609 let mut sequence = Vec::with_capacity(data.len() + 7);
610 sequence.extend_from_slice(b"\x1b]52;");
611 sequence.extend_from_slice(data.as_bytes());
612 match end {
613 InputEndType::Bel => sequence.push(b'\x07'),
614 InputEndType::St => sequence.extend_from_slice(b"\x1b\\"),
615 }
616 self.push_terminal_passthrough(TerminalPassthrough::clipboard(sequence));
617 }
618 fn osc_reset_palette(&mut self, _data: &str) {}
619 fn osc_reset_fg(&mut self) {}
620 fn osc_reset_bg(&mut self) {}
621 fn osc_reset_cursor(&mut self) {}
622 fn osc_shell_integration(&mut self, _data: &str) {}
623}
624
625#[cfg(test)]
626mod tests {
627 #[test]
628 fn cursor_backward_tab_steps_are_bounded_by_cursor_column() {
629 assert_eq!(super::cursor_backward_tab_steps(0, 0), 1);
630 assert_eq!(super::cursor_backward_tab_steps(1, 7), 1);
631 assert_eq!(super::cursor_backward_tab_steps(u32::MAX, 7), 8);
632 }
633}