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