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