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