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