Skip to main content

rmux_core/screen/
writer.rs

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}