terminal_element.rs

  1use alacritty_terminal::{
  2    grid::{Dimensions, GridIterator, Indexed, Scroll},
  3    index::{Column as GridCol, Line as GridLine, Point, Side},
  4    selection::{Selection, SelectionRange, SelectionType},
  5    sync::FairMutex,
  6    term::{
  7        cell::{Cell, Flags},
  8        SizeInfo,
  9    },
 10    Term,
 11};
 12use editor::{Cursor, CursorShape, HighlightedRange, HighlightedRangeLine};
 13use gpui::{
 14    color::Color,
 15    elements::*,
 16    fonts::{TextStyle, Underline},
 17    geometry::{
 18        rect::RectF,
 19        vector::{vec2f, Vector2F},
 20    },
 21    json::json,
 22    text_layout::{Line, RunStyle},
 23    Event, FontCache, KeyDownEvent, MouseRegion, PaintContext, Quad, ScrollWheelEvent,
 24    SizeConstraint, TextLayoutCache, WeakModelHandle,
 25};
 26use itertools::Itertools;
 27use ordered_float::OrderedFloat;
 28use settings::Settings;
 29use theme::TerminalStyle;
 30
 31use std::{cmp::min, ops::Range, rc::Rc, sync::Arc};
 32use std::{fmt::Debug, ops::Sub};
 33
 34use crate::{color_translation::convert_color, connection::TerminalConnection, ZedListener};
 35
 36///Scrolling is unbearably sluggish by default. Alacritty supports a configurable
 37///Scroll multiplier that is set to 3 by default. This will be removed when I
 38///Implement scroll bars.
 39const ALACRITTY_SCROLL_MULTIPLIER: f32 = 3.;
 40
 41///Used to display the grid as passed to Alacritty and the TTY.
 42///Useful for debugging inconsistencies between behavior and display
 43#[cfg(debug_assertions)]
 44const DEBUG_GRID: bool = false;
 45
 46///The GPUI element that paints the terminal.
 47///We need to keep a reference to the view for mouse events, do we need it for any other terminal stuff, or can we move that to connection?
 48pub struct TerminalEl {
 49    connection: WeakModelHandle<TerminalConnection>,
 50    view_id: usize,
 51    modal: bool,
 52}
 53
 54///New type pattern so I don't mix these two up
 55struct CellWidth(f32);
 56struct LineHeight(f32);
 57
 58struct LayoutLine {
 59    cells: Vec<LayoutCell>,
 60    highlighted_range: Option<Range<usize>>,
 61}
 62
 63///New type pattern to ensure that we use adjusted mouse positions throughout the code base, rather than
 64struct PaneRelativePos(Vector2F);
 65
 66///Functionally the constructor for the PaneRelativePos type, mutates the mouse_position
 67fn relative_pos(mouse_position: Vector2F, origin: Vector2F) -> PaneRelativePos {
 68    PaneRelativePos(mouse_position.sub(origin)) //Avoid the extra allocation by mutating
 69}
 70
 71#[derive(Clone, Debug, Default)]
 72struct LayoutCell {
 73    point: Point<i32, i32>,
 74    text: Line, //NOTE TO SELF THIS IS BAD PERFORMANCE RN!
 75    background_color: Color,
 76}
 77
 78impl LayoutCell {
 79    fn new(point: Point<i32, i32>, text: Line, background_color: Color) -> LayoutCell {
 80        LayoutCell {
 81            point,
 82            text,
 83            background_color,
 84        }
 85    }
 86}
 87
 88///The information generated during layout that is nescessary for painting
 89pub struct LayoutState {
 90    layout_lines: Vec<LayoutLine>,
 91    line_height: LineHeight,
 92    em_width: CellWidth,
 93    cursor: Option<Cursor>,
 94    background_color: Color,
 95    cur_size: SizeInfo,
 96    terminal: Arc<FairMutex<Term<ZedListener>>>,
 97    selection_color: Color,
 98}
 99
100impl TerminalEl {
101    pub fn new(
102        view_id: usize,
103        connection: WeakModelHandle<TerminalConnection>,
104        modal: bool,
105    ) -> TerminalEl {
106        TerminalEl {
107            view_id,
108            connection,
109            modal,
110        }
111    }
112}
113
114impl Element for TerminalEl {
115    type LayoutState = LayoutState;
116    type PaintState = ();
117
118    fn layout(
119        &mut self,
120        constraint: gpui::SizeConstraint,
121        cx: &mut gpui::LayoutContext,
122    ) -> (gpui::geometry::vector::Vector2F, Self::LayoutState) {
123        //Settings immutably borrows cx here for the settings and font cache
124        //and we need to modify the cx to resize the terminal. So instead of
125        //storing Settings or the font_cache(), we toss them ASAP and then reborrow later
126        let text_style = make_text_style(cx.font_cache(), cx.global::<Settings>());
127        let line_height = LineHeight(cx.font_cache().line_height(text_style.font_size));
128        let cell_width = CellWidth(
129            cx.font_cache()
130                .em_advance(text_style.font_id, text_style.font_size),
131        );
132        let connection_handle = self.connection.upgrade(cx).unwrap();
133
134        //Tell the view our new size. Requires a mutable borrow of cx and the view
135        let cur_size = make_new_size(constraint, &cell_width, &line_height);
136        //Note that set_size locks and mutates the terminal.
137        connection_handle.update(cx.app, |connection, _| connection.set_size(cur_size));
138
139        let (selection_color, terminal_theme) = {
140            let theme = &(cx.global::<Settings>()).theme;
141            (theme.editor.selection.selection, &theme.terminal)
142        };
143
144        let terminal_mutex = connection_handle.read(cx).term.clone();
145        let term = terminal_mutex.lock();
146        let grid = term.grid();
147        let cursor_point = grid.cursor.point;
148        let cursor_text = grid[cursor_point.line][cursor_point.column].c.to_string();
149
150        let content = term.renderable_content();
151
152        let layout_lines = layout_lines(
153            content.display_iter,
154            &text_style,
155            terminal_theme,
156            cx.text_layout_cache,
157            self.modal,
158            content.selection,
159        );
160
161        let block_text = cx.text_layout_cache.layout_str(
162            &cursor_text,
163            text_style.font_size,
164            &[(
165                cursor_text.len(),
166                RunStyle {
167                    font_id: text_style.font_id,
168                    color: terminal_theme.colors.background,
169                    underline: Default::default(),
170                },
171            )],
172        );
173
174        let cursor = get_cursor_shape(
175            content.cursor.point.line.0 as usize,
176            content.cursor.point.column.0 as usize,
177            content.display_offset,
178            &line_height,
179            &cell_width,
180            cur_size.total_lines(),
181            &block_text,
182        )
183        .map(move |(cursor_position, block_width)| {
184            let block_width = if block_width != 0.0 {
185                block_width
186            } else {
187                cell_width.0
188            };
189
190            Cursor::new(
191                cursor_position,
192                block_width,
193                line_height.0,
194                terminal_theme.colors.cursor,
195                CursorShape::Block,
196                Some(block_text.clone()),
197            )
198        });
199        drop(term);
200
201        let background_color = if self.modal {
202            terminal_theme.colors.modal_background
203        } else {
204            terminal_theme.colors.background
205        };
206
207        (
208            constraint.max,
209            LayoutState {
210                layout_lines,
211                line_height,
212                em_width: cell_width,
213                cursor,
214                cur_size,
215                background_color,
216                terminal: terminal_mutex,
217                selection_color,
218            },
219        )
220    }
221
222    fn paint(
223        &mut self,
224        bounds: gpui::geometry::rect::RectF,
225        visible_bounds: gpui::geometry::rect::RectF,
226        layout: &mut Self::LayoutState,
227        cx: &mut gpui::PaintContext,
228    ) -> Self::PaintState {
229        //Setup element stuff
230        let clip_bounds = Some(visible_bounds);
231
232        cx.paint_layer(clip_bounds, |cx| {
233            let cur_size = layout.cur_size.clone();
234            let origin = bounds.origin() + vec2f(layout.em_width.0, 0.);
235
236            //Elements are ephemeral, only at paint time do we know what could be clicked by a mouse
237            attach_mouse_handlers(
238                origin,
239                cur_size,
240                self.view_id,
241                &layout.terminal,
242                visible_bounds,
243                cx,
244            );
245
246            cx.paint_layer(clip_bounds, |cx| {
247                //Start with a background color
248                cx.scene.push_quad(Quad {
249                    bounds: RectF::new(bounds.origin(), bounds.size()),
250                    background: Some(layout.background_color),
251                    border: Default::default(),
252                    corner_radius: 0.,
253                });
254
255                //Draw cell backgrounds
256                for layout_line in &layout.layout_lines {
257                    for layout_cell in &layout_line.cells {
258                        let position = vec2f(
259                            origin.x() + layout_cell.point.column as f32 * layout.em_width.0,
260                            origin.y() + layout_cell.point.line as f32 * layout.line_height.0,
261                        );
262                        let size = vec2f(layout.em_width.0, layout.line_height.0);
263
264                        cx.scene.push_quad(Quad {
265                            bounds: RectF::new(position, size),
266                            background: Some(layout_cell.background_color),
267                            border: Default::default(),
268                            corner_radius: 0.,
269                        })
270                    }
271                }
272            });
273
274            //Draw Selection
275            cx.paint_layer(clip_bounds, |cx| {
276                let mut highlight_y = None;
277                let highlight_lines = layout
278                    .layout_lines
279                    .iter()
280                    .filter_map(|line| {
281                        if let Some(range) = &line.highlighted_range {
282                            if let None = highlight_y {
283                                highlight_y = Some(
284                                    origin.y()
285                                        + line.cells[0].point.line as f32 * layout.line_height.0,
286                                );
287                            }
288                            let start_x = origin.x()
289                                + line.cells[range.start].point.column as f32 * layout.em_width.0;
290                            let end_x = origin.x()
291                                + line.cells[range.end].point.column as f32 * layout.em_width.0
292                                + layout.em_width.0;
293
294                            return Some(HighlightedRangeLine { start_x, end_x });
295                        } else {
296                            return None;
297                        }
298                    })
299                    .collect::<Vec<HighlightedRangeLine>>();
300
301                if let Some(y) = highlight_y {
302                    let hr = HighlightedRange {
303                        start_y: y, //Need to change this
304                        line_height: layout.line_height.0,
305                        lines: highlight_lines,
306                        color: layout.selection_color,
307                        //Copied from editor. TODO: move to theme or something
308                        corner_radius: 0.15 * layout.line_height.0,
309                    };
310                    hr.paint(bounds, cx.scene);
311                }
312            });
313
314            cx.paint_layer(clip_bounds, |cx| {
315                for layout_line in &layout.layout_lines {
316                    for layout_cell in &layout_line.cells {
317                        let point = layout_cell.point;
318
319                        //Don't actually know the start_x for a line, until here:
320                        let cell_origin = vec2f(
321                            origin.x() + point.column as f32 * layout.em_width.0,
322                            origin.y() + point.line as f32 * layout.line_height.0,
323                        );
324
325                        layout_cell.text.paint(
326                            cell_origin,
327                            visible_bounds,
328                            layout.line_height.0,
329                            cx,
330                        );
331                    }
332                }
333            });
334
335            //Draw cursor
336            if let Some(cursor) = &layout.cursor {
337                cx.paint_layer(clip_bounds, |cx| {
338                    cursor.paint(origin, cx);
339                })
340            }
341
342            #[cfg(debug_assertions)]
343            if DEBUG_GRID {
344                cx.paint_layer(clip_bounds, |cx| {
345                    draw_debug_grid(bounds, layout, cx);
346                })
347            }
348        });
349    }
350
351    fn dispatch_event(
352        &mut self,
353        event: &gpui::Event,
354        _bounds: gpui::geometry::rect::RectF,
355        visible_bounds: gpui::geometry::rect::RectF,
356        layout: &mut Self::LayoutState,
357        _paint: &mut Self::PaintState,
358        cx: &mut gpui::EventContext,
359    ) -> bool {
360        match event {
361            Event::ScrollWheel(ScrollWheelEvent {
362                delta, position, ..
363            }) => visible_bounds
364                .contains_point(*position)
365                .then(|| {
366                    let vertical_scroll =
367                        (delta.y() / layout.line_height.0) * ALACRITTY_SCROLL_MULTIPLIER;
368
369                    if let Some(connection) = self.connection.upgrade(cx.app) {
370                        connection.update(cx.app, |connection, _| {
371                            connection
372                                .term
373                                .lock()
374                                .scroll_display(Scroll::Delta(vertical_scroll.round() as i32));
375                        })
376                    }
377                })
378                .is_some(),
379            Event::KeyDown(KeyDownEvent { keystroke, .. }) => {
380                if !cx.is_parent_view_focused() {
381                    return false;
382                }
383
384                self.connection
385                    .upgrade(cx.app)
386                    .map(|connection| {
387                        connection
388                            .update(cx.app, |connection, _| connection.try_keystroke(keystroke))
389                    })
390                    .unwrap_or(false)
391            }
392            _ => false,
393        }
394    }
395
396    fn debug(
397        &self,
398        _bounds: gpui::geometry::rect::RectF,
399        _layout: &Self::LayoutState,
400        _paint: &Self::PaintState,
401        _cx: &gpui::DebugContext,
402    ) -> gpui::serde_json::Value {
403        json!({
404            "type": "TerminalElement",
405        })
406    }
407}
408
409pub fn mouse_to_cell_data(
410    pos: Vector2F,
411    origin: Vector2F,
412    cur_size: SizeInfo,
413    display_offset: usize,
414) -> (Point, alacritty_terminal::index::Direction) {
415    let relative_pos = relative_pos(pos, origin);
416    let point = grid_cell(&relative_pos, cur_size, display_offset);
417    let side = cell_side(&relative_pos, cur_size);
418    (point, side)
419}
420
421///Configures a text style from the current settings.
422fn make_text_style(font_cache: &FontCache, settings: &Settings) -> TextStyle {
423    TextStyle {
424        color: settings.theme.editor.text_color,
425        font_family_id: settings.buffer_font_family,
426        font_family_name: font_cache.family_name(settings.buffer_font_family).unwrap(),
427        font_id: font_cache
428            .select_font(settings.buffer_font_family, &Default::default())
429            .unwrap(),
430        font_size: settings.buffer_font_size,
431        font_properties: Default::default(),
432        underline: Default::default(),
433    }
434}
435
436///Configures a size info object from the given information.
437fn make_new_size(
438    constraint: SizeConstraint,
439    cell_width: &CellWidth,
440    line_height: &LineHeight,
441) -> SizeInfo {
442    SizeInfo::new(
443        constraint.max.x() - cell_width.0,
444        constraint.max.y(),
445        cell_width.0,
446        line_height.0,
447        0.,
448        0.,
449        false,
450    )
451}
452
453fn layout_lines(
454    grid: GridIterator<Cell>,
455    text_style: &TextStyle,
456    terminal_theme: &TerminalStyle,
457    text_layout_cache: &TextLayoutCache,
458    modal: bool,
459    selection_range: Option<SelectionRange>,
460) -> Vec<LayoutLine> {
461    let lines = grid.group_by(|i| i.point.line);
462    lines
463        .into_iter()
464        .enumerate()
465        .map(|(line_index, (_, line))| {
466            let mut highlighted_range = None;
467            let cells = line
468                .enumerate()
469                .map(|(x_index, indexed_cell)| {
470                    if selection_range
471                        .map(|range| range.contains(indexed_cell.point))
472                        .unwrap_or(false)
473                    {
474                        let mut range = highlighted_range.take().unwrap_or(x_index..x_index);
475                        range.end = range.end.max(x_index);
476                        highlighted_range = Some(range);
477                    }
478
479                    let cell_text = &indexed_cell.c.to_string();
480
481                    let cell_style = cell_style(&indexed_cell, terminal_theme, text_style, modal);
482
483                    //This is where we might be able to get better performance
484                    let layout_cell = text_layout_cache.layout_str(
485                        cell_text,
486                        text_style.font_size,
487                        &[(cell_text.len(), cell_style)],
488                    );
489
490                    LayoutCell::new(
491                        Point::new(line_index as i32, indexed_cell.point.column.0 as i32),
492                        layout_cell,
493                        convert_color(&indexed_cell.bg, &terminal_theme.colors, modal),
494                    )
495                })
496                .collect::<Vec<LayoutCell>>();
497
498            LayoutLine {
499                cells,
500                highlighted_range,
501            }
502        })
503        .collect::<Vec<LayoutLine>>()
504}
505
506// Compute the cursor position and expected block width, may return a zero width if x_for_index returns
507// the same position for sequential indexes. Use em_width instead
508//TODO: This function is messy, too many arguments and too many ifs. Simplify.
509fn get_cursor_shape(
510    line: usize,
511    line_index: usize,
512    display_offset: usize,
513    line_height: &LineHeight,
514    cell_width: &CellWidth,
515    total_lines: usize,
516    text_fragment: &Line,
517) -> Option<(Vector2F, f32)> {
518    let cursor_line = line + display_offset;
519    if cursor_line <= total_lines {
520        let cursor_width = if text_fragment.width() == 0. {
521            cell_width.0
522        } else {
523            text_fragment.width()
524        };
525
526        Some((
527            vec2f(
528                line_index as f32 * cell_width.0,
529                cursor_line as f32 * line_height.0,
530            ),
531            cursor_width,
532        ))
533    } else {
534        None
535    }
536}
537
538///Convert the Alacritty cell styles to GPUI text styles and background color
539fn cell_style(
540    indexed: &Indexed<&Cell>,
541    style: &TerminalStyle,
542    text_style: &TextStyle,
543    modal: bool,
544) -> RunStyle {
545    let flags = indexed.cell.flags;
546    let fg = convert_color(&indexed.cell.fg, &style.colors, modal);
547
548    let underline = flags
549        .contains(Flags::UNDERLINE)
550        .then(|| Underline {
551            color: Some(fg),
552            squiggly: false,
553            thickness: OrderedFloat(1.),
554        })
555        .unwrap_or_default();
556
557    RunStyle {
558        color: fg,
559        font_id: text_style.font_id,
560        underline,
561    }
562}
563
564fn attach_mouse_handlers(
565    origin: Vector2F,
566    cur_size: SizeInfo,
567    view_id: usize,
568    terminal_mutex: &Arc<FairMutex<Term<ZedListener>>>,
569    visible_bounds: RectF,
570    cx: &mut PaintContext,
571) {
572    let click_mutex = terminal_mutex.clone();
573    let drag_mutex = terminal_mutex.clone();
574    let mouse_down_mutex = terminal_mutex.clone();
575
576    cx.scene.push_mouse_region(MouseRegion {
577        view_id,
578        mouse_down: Some(Rc::new(move |pos, _| {
579            let mut term = mouse_down_mutex.lock();
580            let (point, side) = mouse_to_cell_data(
581                pos,
582                origin,
583                cur_size,
584                term.renderable_content().display_offset,
585            );
586            term.selection = Some(Selection::new(SelectionType::Simple, point, side))
587        })),
588        click: Some(Rc::new(move |pos, click_count, cx| {
589            let mut term = click_mutex.lock();
590
591            let (point, side) = mouse_to_cell_data(
592                pos,
593                origin,
594                cur_size,
595                term.renderable_content().display_offset,
596            );
597
598            let selection_type = match click_count {
599                0 => return, //This is a release
600                1 => Some(SelectionType::Simple),
601                2 => Some(SelectionType::Semantic),
602                3 => Some(SelectionType::Lines),
603                _ => None,
604            };
605
606            let selection =
607                selection_type.map(|selection_type| Selection::new(selection_type, point, side));
608
609            term.selection = selection;
610            cx.focus_parent_view();
611            cx.notify();
612        })),
613        bounds: visible_bounds,
614        drag: Some(Rc::new(move |_delta, pos, cx| {
615            let mut term = drag_mutex.lock();
616
617            let (point, side) = mouse_to_cell_data(
618                pos,
619                origin,
620                cur_size,
621                term.renderable_content().display_offset,
622            );
623
624            if let Some(mut selection) = term.selection.take() {
625                selection.update(point, side);
626                term.selection = Some(selection);
627            }
628
629            cx.notify();
630        })),
631        ..Default::default()
632    });
633}
634
635///Copied (with modifications) from alacritty/src/input.rs > Processor::cell_side()
636fn cell_side(pos: &PaneRelativePos, cur_size: SizeInfo) -> Side {
637    let x = pos.0.x() as usize;
638    let cell_x = x.saturating_sub(cur_size.cell_width() as usize) % cur_size.cell_width() as usize;
639    let half_cell_width = (cur_size.cell_width() / 2.0) as usize;
640
641    let additional_padding =
642        (cur_size.width() - cur_size.cell_width() * 2.) % cur_size.cell_width();
643    let end_of_grid = cur_size.width() - cur_size.cell_width() - additional_padding;
644
645    if cell_x > half_cell_width
646            // Edge case when mouse leaves the window.
647            || x as f32 >= end_of_grid
648    {
649        Side::Right
650    } else {
651        Side::Left
652    }
653}
654
655///Copied (with modifications) from alacritty/src/event.rs > Mouse::point()
656///Position is a pane-relative position. That means the top left corner of the mouse
657///Region should be (0,0)
658fn grid_cell(pos: &PaneRelativePos, cur_size: SizeInfo, display_offset: usize) -> Point {
659    let pos = pos.0;
660    let col = pos.x() / cur_size.cell_width(); //TODO: underflow...
661    let col = min(GridCol(col as usize), cur_size.last_column());
662
663    let line = pos.y() / cur_size.cell_height();
664    let line = min(line as i32, cur_size.bottommost_line().0);
665
666    //when clicking, need to ADD to get to the top left cell
667    //e.g. total_lines - viewport_height, THEN subtract display offset
668    //0 -> total_lines - viewport_height - display_offset + mouse_line
669
670    Point::new(GridLine(line - display_offset as i32), col)
671}
672
673///Draws the grid as Alacritty sees it. Useful for checking if there is an inconsistency between
674///Display and conceptual grid.
675#[cfg(debug_assertions)]
676fn draw_debug_grid(bounds: RectF, layout: &mut LayoutState, cx: &mut PaintContext) {
677    let width = layout.cur_size.width();
678    let height = layout.cur_size.height();
679    //Alacritty uses 'as usize', so shall we.
680    for col in 0..(width / layout.em_width.0).round() as usize {
681        cx.scene.push_quad(Quad {
682            bounds: RectF::new(
683                bounds.origin() + vec2f((col + 1) as f32 * layout.em_width.0, 0.),
684                vec2f(1., height),
685            ),
686            background: Some(Color::green()),
687            border: Default::default(),
688            corner_radius: 0.,
689        });
690    }
691    for row in 0..((height / layout.line_height.0) + 1.0).round() as usize {
692        cx.scene.push_quad(Quad {
693            bounds: RectF::new(
694                bounds.origin() + vec2f(layout.em_width.0, row as f32 * layout.line_height.0),
695                vec2f(width, 1.),
696            ),
697            background: Some(Color::green()),
698            border: Default::default(),
699            corner_radius: 0.,
700        });
701    }
702}
703
704mod test {
705
706    #[test]
707    fn test_mouse_to_selection() {
708        let term_width = 100.;
709        let term_height = 200.;
710        let cell_width = 10.;
711        let line_height = 20.;
712        let mouse_pos_x = 100.; //Window relative
713        let mouse_pos_y = 100.; //Window relative
714        let origin_x = 10.;
715        let origin_y = 20.;
716
717        let cur_size = alacritty_terminal::term::SizeInfo::new(
718            term_width,
719            term_height,
720            cell_width,
721            line_height,
722            0.,
723            0.,
724            false,
725        );
726
727        let mouse_pos = gpui::geometry::vector::vec2f(mouse_pos_x, mouse_pos_y);
728        let origin = gpui::geometry::vector::vec2f(origin_x, origin_y); //Position of terminal window, 1 'cell' in
729        let (point, _) =
730            crate::terminal_element::mouse_to_cell_data(mouse_pos, origin, cur_size, 0);
731        assert_eq!(
732            point,
733            alacritty_terminal::index::Point::new(
734                alacritty_terminal::index::Line(((mouse_pos_y - origin_y) / line_height) as i32),
735                alacritty_terminal::index::Column(((mouse_pos_x - origin_x) / cell_width) as usize),
736            )
737        );
738    }
739
740    #[test]
741    fn test_mouse_to_selection_off_edge() {
742        let term_width = 100.;
743        let term_height = 200.;
744        let cell_width = 10.;
745        let line_height = 20.;
746        let mouse_pos_x = 100.; //Window relative
747        let mouse_pos_y = 100.; //Window relative
748        let origin_x = 10.;
749        let origin_y = 20.;
750
751        let cur_size = alacritty_terminal::term::SizeInfo::new(
752            term_width,
753            term_height,
754            cell_width,
755            line_height,
756            0.,
757            0.,
758            false,
759        );
760
761        let mouse_pos = gpui::geometry::vector::vec2f(mouse_pos_x, mouse_pos_y);
762        let origin = gpui::geometry::vector::vec2f(origin_x, origin_y); //Position of terminal window, 1 'cell' in
763        let (point, _) =
764            crate::terminal_element::mouse_to_cell_data(mouse_pos, origin, cur_size, 0);
765        assert_eq!(
766            point,
767            alacritty_terminal::index::Point::new(
768                alacritty_terminal::index::Line(((mouse_pos_y - origin_y) / line_height) as i32),
769                alacritty_terminal::index::Column(((mouse_pos_x - origin_x) / cell_width) as usize),
770            )
771        );
772    }
773}