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