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        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}