1use alacritty_terminal::{
2 ansi::{Color::Named, NamedColor},
3 grid::{Dimensions, Scroll},
4 index::{Column as GridCol, Line as GridLine, Point, Side},
5 selection::SelectionRange,
6 term::cell::{Cell, Flags},
7};
8use editor::{Cursor, CursorShape, HighlightedRange, HighlightedRangeLine};
9use gpui::{
10 color::Color,
11 elements::*,
12 fonts::{TextStyle, Underline},
13 geometry::{
14 rect::RectF,
15 vector::{vec2f, Vector2F},
16 },
17 json::json,
18 text_layout::{Line, RunStyle},
19 Event, FontCache, KeyDownEvent, MouseButton, MouseButtonEvent, MouseMovedEvent, MouseRegion,
20 PaintContext, Quad, ScrollWheelEvent, TextLayoutCache, WeakModelHandle, WeakViewHandle,
21};
22use itertools::Itertools;
23use ordered_float::OrderedFloat;
24use settings::Settings;
25use theme::TerminalStyle;
26use util::ResultExt;
27
28use std::{
29 cmp::min,
30 ops::{Deref, Range},
31};
32use std::{fmt::Debug, ops::Sub};
33
34use crate::{
35 connected_view::ConnectedView, mappings::colors::convert_color, Terminal, TerminalSize,
36};
37
38///Scrolling is unbearably sluggish by default. Alacritty supports a configurable
39///Scroll multiplier that is set to 3 by default. This will be removed when I
40///Implement scroll bars.
41pub const ALACRITTY_SCROLL_MULTIPLIER: f32 = 3.;
42
43///The information generated during layout that is nescessary for painting
44pub struct LayoutState {
45 cells: Vec<LayoutCell>,
46 rects: Vec<LayoutRect>,
47 highlights: Vec<RelativeHighlightedRange>,
48 cursor: Option<Cursor>,
49 background_color: Color,
50 selection_color: Color,
51 size: TerminalSize,
52 display_offset: usize,
53}
54
55struct IndexedCell {
56 point: Point,
57 cell: Cell,
58}
59
60impl Deref for IndexedCell {
61 type Target = Cell;
62
63 #[inline]
64 fn deref(&self) -> &Cell {
65 &self.cell
66 }
67}
68
69///Helper struct for converting data between alacritty's cursor points, and displayed cursor points
70struct DisplayCursor {
71 line: i32,
72 col: usize,
73}
74
75impl DisplayCursor {
76 fn from(cursor_point: Point, display_offset: usize) -> Self {
77 Self {
78 line: cursor_point.line.0 + display_offset as i32,
79 col: cursor_point.column.0,
80 }
81 }
82
83 pub fn line(&self) -> i32 {
84 self.line
85 }
86
87 pub fn col(&self) -> usize {
88 self.col
89 }
90}
91
92#[derive(Clone, Debug, Default)]
93struct LayoutCell {
94 point: Point<i32, i32>,
95 text: Line,
96}
97
98impl LayoutCell {
99 fn new(point: Point<i32, i32>, text: Line) -> LayoutCell {
100 LayoutCell { point, text }
101 }
102
103 fn paint(
104 &self,
105 origin: Vector2F,
106 layout: &LayoutState,
107 visible_bounds: RectF,
108 cx: &mut PaintContext,
109 ) {
110 let pos = point_to_absolute(origin, self.point, layout);
111 self.text
112 .paint(pos, visible_bounds, layout.size.line_height, cx);
113 }
114}
115
116#[derive(Clone, Debug, Default)]
117struct LayoutRect {
118 point: Point<i32, i32>,
119 num_of_cells: usize,
120 color: Color,
121}
122
123impl LayoutRect {
124 fn new(point: Point<i32, i32>, num_of_cells: usize, color: Color) -> LayoutRect {
125 LayoutRect {
126 point,
127 num_of_cells,
128 color,
129 }
130 }
131
132 fn extend(&self) -> Self {
133 LayoutRect {
134 point: self.point,
135 num_of_cells: self.num_of_cells + 1,
136 color: self.color,
137 }
138 }
139
140 fn paint(&self, origin: Vector2F, layout: &LayoutState, cx: &mut PaintContext) {
141 let position = point_to_absolute(origin, self.point, layout);
142
143 let size = vec2f(
144 (layout.size.cell_width.ceil() * self.num_of_cells as f32).ceil(),
145 layout.size.line_height,
146 );
147
148 cx.scene.push_quad(Quad {
149 bounds: RectF::new(position, size),
150 background: Some(self.color),
151 border: Default::default(),
152 corner_radius: 0.,
153 })
154 }
155}
156
157fn point_to_absolute(origin: Vector2F, point: Point<i32, i32>, layout: &LayoutState) -> Vector2F {
158 vec2f(
159 (origin.x() + point.column as f32 * layout.size.cell_width).floor(),
160 origin.y() + point.line as f32 * layout.size.line_height,
161 )
162}
163
164#[derive(Clone, Debug, Default)]
165struct RelativeHighlightedRange {
166 line_index: usize,
167 range: Range<usize>,
168}
169
170impl RelativeHighlightedRange {
171 fn new(line_index: usize, range: Range<usize>) -> Self {
172 RelativeHighlightedRange { line_index, range }
173 }
174
175 fn to_highlighted_range_line(
176 &self,
177 origin: Vector2F,
178 layout: &LayoutState,
179 ) -> HighlightedRangeLine {
180 let start_x = origin.x() + self.range.start as f32 * layout.size.cell_width;
181 let end_x =
182 origin.x() + self.range.end as f32 * layout.size.cell_width + layout.size.cell_width;
183
184 return HighlightedRangeLine { start_x, end_x };
185 }
186}
187
188///The GPUI element that paints the terminal.
189///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?
190pub struct TerminalEl {
191 terminal: WeakModelHandle<Terminal>,
192 view: WeakViewHandle<ConnectedView>,
193 modal: bool,
194}
195
196impl TerminalEl {
197 pub fn new(
198 view: WeakViewHandle<ConnectedView>,
199 terminal: WeakModelHandle<Terminal>,
200 modal: bool,
201 ) -> TerminalEl {
202 TerminalEl {
203 view,
204 terminal,
205 modal,
206 }
207 }
208
209 fn layout_grid(
210 grid: Vec<IndexedCell>,
211 text_style: &TextStyle,
212 terminal_theme: &TerminalStyle,
213 text_layout_cache: &TextLayoutCache,
214 modal: bool,
215 selection_range: Option<SelectionRange>,
216 ) -> (
217 Vec<LayoutCell>,
218 Vec<LayoutRect>,
219 Vec<RelativeHighlightedRange>,
220 ) {
221 let mut cells = vec![];
222 let mut rects = vec![];
223 let mut highlight_ranges = vec![];
224
225 let mut cur_rect: Option<LayoutRect> = None;
226 let mut cur_alac_color = None;
227 let mut highlighted_range = None;
228
229 let linegroups = grid.into_iter().group_by(|i| i.point.line);
230 for (line_index, (_, line)) in linegroups.into_iter().enumerate() {
231 for (x_index, cell) in line.enumerate() {
232 //Increase selection range
233 {
234 if selection_range
235 .map(|range| range.contains(cell.point))
236 .unwrap_or(false)
237 {
238 let mut range = highlighted_range.take().unwrap_or(x_index..x_index);
239 range.end = range.end.max(x_index);
240 highlighted_range = Some(range);
241 }
242 }
243
244 //Expand background rect range
245 {
246 if matches!(cell.bg, Named(NamedColor::Background)) {
247 //Continue to next cell, resetting variables if nescessary
248 cur_alac_color = None;
249 if let Some(rect) = cur_rect {
250 rects.push(rect);
251 cur_rect = None
252 }
253 } else {
254 match cur_alac_color {
255 Some(cur_color) => {
256 if cell.cell.bg == cur_color {
257 cur_rect = cur_rect.take().map(|rect| rect.extend());
258 } else {
259 cur_alac_color = Some(cell.bg);
260 if let Some(_) = cur_rect {
261 rects.push(cur_rect.take().unwrap());
262 }
263 cur_rect = Some(LayoutRect::new(
264 Point::new(line_index as i32, cell.point.column.0 as i32),
265 1,
266 convert_color(&cell.bg, &terminal_theme.colors, modal),
267 ));
268 }
269 }
270 None => {
271 cur_alac_color = Some(cell.bg);
272 cur_rect = Some(LayoutRect::new(
273 Point::new(line_index as i32, cell.point.column.0 as i32),
274 1,
275 convert_color(&cell.bg, &terminal_theme.colors, modal),
276 ));
277 }
278 }
279 }
280 }
281
282 //Layout current cell text
283 {
284 let cell_text = &cell.c.to_string();
285 if cell_text != " " {
286 let cell_style =
287 TerminalEl::cell_style(&cell, terminal_theme, text_style, modal);
288
289 let layout_cell = text_layout_cache.layout_str(
290 cell_text,
291 text_style.font_size,
292 &[(cell_text.len(), cell_style)],
293 );
294
295 cells.push(LayoutCell::new(
296 Point::new(line_index as i32, cell.point.column.0 as i32),
297 layout_cell,
298 ))
299 }
300 };
301 }
302
303 if highlighted_range.is_some() {
304 highlight_ranges.push(RelativeHighlightedRange::new(
305 line_index,
306 highlighted_range.take().unwrap(),
307 ))
308 }
309
310 if cur_rect.is_some() {
311 rects.push(cur_rect.take().unwrap());
312 }
313 }
314
315 (cells, rects, highlight_ranges)
316 }
317
318 // Compute the cursor position and expected block width, may return a zero width if x_for_index returns
319 // the same position for sequential indexes. Use em_width instead
320 fn shape_cursor(
321 cursor_point: DisplayCursor,
322 size: TerminalSize,
323 text_fragment: &Line,
324 ) -> Option<(Vector2F, f32)> {
325 if cursor_point.line() < size.total_lines() as i32 {
326 let cursor_width = if text_fragment.width() == 0. {
327 size.cell_width()
328 } else {
329 text_fragment.width()
330 };
331
332 Some((
333 vec2f(
334 cursor_point.col() as f32 * size.cell_width(),
335 cursor_point.line() as f32 * size.line_height(),
336 ),
337 cursor_width,
338 ))
339 } else {
340 None
341 }
342 }
343
344 ///Convert the Alacritty cell styles to GPUI text styles and background color
345 fn cell_style(
346 indexed: &IndexedCell,
347 style: &TerminalStyle,
348 text_style: &TextStyle,
349 modal: bool,
350 ) -> RunStyle {
351 let flags = indexed.cell.flags;
352 let fg = convert_color(&indexed.cell.fg, &style.colors, modal);
353
354 let underline = flags
355 .contains(Flags::UNDERLINE)
356 .then(|| Underline {
357 color: Some(fg),
358 squiggly: false,
359 thickness: OrderedFloat(1.),
360 })
361 .unwrap_or_default();
362
363 RunStyle {
364 color: fg,
365 font_id: text_style.font_id,
366 underline,
367 }
368 }
369
370 fn attach_mouse_handlers(
371 &self,
372 origin: Vector2F,
373 view_id: usize,
374 visible_bounds: RectF,
375 cur_size: TerminalSize,
376 display_offset: usize,
377 cx: &mut PaintContext,
378 ) {
379 let mouse_down_connection = self.terminal.clone();
380 let click_connection = self.terminal.clone();
381 let drag_connection = self.terminal.clone();
382 cx.scene.push_mouse_region(
383 MouseRegion::new(view_id, None, visible_bounds)
384 .on_down(
385 MouseButton::Left,
386 move |MouseButtonEvent { position, .. }, cx| {
387 if let Some(conn_handle) = mouse_down_connection.upgrade(cx.app) {
388 conn_handle.update(cx.app, |terminal, cx| {
389 let (point, side) = TerminalEl::mouse_to_cell_data(
390 position,
391 origin,
392 cur_size,
393 display_offset,
394 );
395
396 terminal.mouse_down(point, side);
397
398 cx.notify();
399 })
400 }
401 },
402 )
403 .on_click(
404 MouseButton::Left,
405 move |MouseButtonEvent {
406 position,
407 click_count,
408 ..
409 },
410 cx| {
411 cx.focus_parent_view();
412 if let Some(conn_handle) = click_connection.upgrade(cx.app) {
413 conn_handle.update(cx.app, |terminal, cx| {
414 let (point, side) = TerminalEl::mouse_to_cell_data(
415 position,
416 origin,
417 cur_size,
418 display_offset,
419 );
420
421 terminal.click(point, side, click_count);
422
423 cx.notify();
424 });
425 }
426 },
427 )
428 .on_drag(
429 MouseButton::Left,
430 move |_, MouseMovedEvent { position, .. }, cx| {
431 if let Some(conn_handle) = drag_connection.upgrade(cx.app) {
432 conn_handle.update(cx.app, |terminal, cx| {
433 let (point, side) = TerminalEl::mouse_to_cell_data(
434 position,
435 origin,
436 cur_size,
437 display_offset,
438 );
439
440 terminal.drag(point, side);
441
442 cx.notify()
443 });
444 }
445 },
446 ),
447 );
448 }
449
450 ///Configures a text style from the current settings.
451 pub fn make_text_style(font_cache: &FontCache, settings: &Settings) -> TextStyle {
452 // Pull the font family from settings properly overriding
453 let family_id = settings
454 .terminal_overrides
455 .font_family
456 .as_ref()
457 .or_else(|| settings.terminal_defaults.font_family.as_ref())
458 .and_then(|family_name| font_cache.load_family(&[family_name]).log_err())
459 .unwrap_or(settings.buffer_font_family);
460
461 let font_size = settings
462 .terminal_overrides
463 .font_size
464 .or(settings.terminal_defaults.font_size)
465 .unwrap_or(settings.buffer_font_size);
466
467 let font_id = font_cache
468 .select_font(family_id, &Default::default())
469 .unwrap();
470
471 TextStyle {
472 color: settings.theme.editor.text_color,
473 font_family_id: family_id,
474 font_family_name: font_cache.family_name(family_id).unwrap(),
475 font_id,
476 font_size,
477 font_properties: Default::default(),
478 underline: Default::default(),
479 }
480 }
481
482 pub fn mouse_to_cell_data(
483 pos: Vector2F,
484 origin: Vector2F,
485 cur_size: TerminalSize,
486 display_offset: usize,
487 ) -> (Point, alacritty_terminal::index::Direction) {
488 let pos = pos.sub(origin);
489 let point = {
490 let col = pos.x() / cur_size.cell_width; //TODO: underflow...
491 let col = min(GridCol(col as usize), cur_size.last_column());
492
493 let line = pos.y() / cur_size.line_height;
494 let line = min(line as i32, cur_size.bottommost_line().0);
495
496 Point::new(GridLine(line - display_offset as i32), col)
497 };
498
499 //Copied (with modifications) from alacritty/src/input.rs > Processor::cell_side()
500 let side = {
501 let x = pos.0.x() as usize;
502 let cell_x =
503 x.saturating_sub(cur_size.cell_width as usize) % cur_size.cell_width as usize;
504 let half_cell_width = (cur_size.cell_width / 2.0) as usize;
505
506 let additional_padding =
507 (cur_size.width() - cur_size.cell_width * 2.) % cur_size.cell_width;
508 let end_of_grid = cur_size.width() - cur_size.cell_width - additional_padding;
509 //Width: Pixels or columns?
510 if cell_x > half_cell_width
511 // Edge case when mouse leaves the window.
512 || x as f32 >= end_of_grid
513 {
514 Side::Right
515 } else {
516 Side::Left
517 }
518 };
519
520 (point, side)
521 }
522}
523
524impl Element for TerminalEl {
525 type LayoutState = LayoutState;
526 type PaintState = ();
527
528 fn layout(
529 &mut self,
530 constraint: gpui::SizeConstraint,
531 cx: &mut gpui::LayoutContext,
532 ) -> (gpui::geometry::vector::Vector2F, Self::LayoutState) {
533 let settings = cx.global::<Settings>();
534 let font_cache = cx.font_cache();
535
536 //Setup layout information
537 let terminal_theme = settings.theme.terminal.clone(); //TODO: Try to minimize this clone.
538 let text_style = TerminalEl::make_text_style(font_cache, &settings);
539 let selection_color = settings.theme.editor.selection.selection;
540 let dimensions = {
541 let line_height = font_cache.line_height(text_style.font_size);
542 let cell_width = font_cache.em_advance(text_style.font_id, text_style.font_size);
543 TerminalSize::new(line_height, cell_width, constraint.max)
544 };
545
546 let background_color = if self.modal {
547 terminal_theme.colors.modal_background.clone()
548 } else {
549 terminal_theme.colors.background.clone()
550 };
551
552 let (cells, selection, cursor, display_offset, cursor_text) = self
553 .terminal
554 .upgrade(cx)
555 .unwrap()
556 .update(cx.app, |terminal, mcx| {
557 terminal.set_size(dimensions);
558 terminal.render_lock(mcx, |content, cursor_text| {
559 let mut cells = vec![];
560 cells.extend(content.display_iter.map(|ic| IndexedCell {
561 point: ic.point.clone(),
562 cell: ic.cell.clone(),
563 }));
564
565 (
566 cells,
567 content.selection.clone(),
568 content.cursor.clone(),
569 content.display_offset.clone(),
570 cursor_text.clone(),
571 )
572 })
573 });
574
575 let (cells, rects, highlights) = TerminalEl::layout_grid(
576 cells,
577 &text_style,
578 &terminal_theme,
579 cx.text_layout_cache,
580 self.modal,
581 selection,
582 );
583
584 //Layout cursor
585 let cursor = {
586 let cursor_point = DisplayCursor::from(cursor.point, display_offset);
587 let cursor_text = {
588 let str_trxt = cursor_text.to_string();
589 cx.text_layout_cache.layout_str(
590 &str_trxt,
591 text_style.font_size,
592 &[(
593 str_trxt.len(),
594 RunStyle {
595 font_id: text_style.font_id,
596 color: terminal_theme.colors.background,
597 underline: Default::default(),
598 },
599 )],
600 )
601 };
602
603 TerminalEl::shape_cursor(cursor_point, dimensions, &cursor_text).map(
604 move |(cursor_position, block_width)| {
605 Cursor::new(
606 cursor_position,
607 block_width,
608 dimensions.line_height,
609 terminal_theme.colors.cursor,
610 CursorShape::Block,
611 Some(cursor_text.clone()),
612 )
613 },
614 )
615 };
616
617 //Done!
618 (
619 constraint.max,
620 LayoutState {
621 cells,
622 cursor,
623 background_color,
624 selection_color,
625 size: dimensions,
626 rects,
627 highlights,
628 display_offset,
629 },
630 )
631 }
632
633 fn paint(
634 &mut self,
635 bounds: gpui::geometry::rect::RectF,
636 visible_bounds: gpui::geometry::rect::RectF,
637 layout: &mut Self::LayoutState,
638 cx: &mut gpui::PaintContext,
639 ) -> Self::PaintState {
640 //Setup element stuff
641 let clip_bounds = Some(visible_bounds);
642
643 cx.paint_layer(clip_bounds, |cx| {
644 let origin = bounds.origin() + vec2f(layout.size.cell_width, 0.);
645
646 //Elements are ephemeral, only at paint time do we know what could be clicked by a mouse
647 self.attach_mouse_handlers(
648 origin,
649 self.view.id(),
650 visible_bounds,
651 layout.size,
652 layout.display_offset,
653 cx,
654 );
655
656 cx.paint_layer(clip_bounds, |cx| {
657 //Start with a background color
658 cx.scene.push_quad(Quad {
659 bounds: RectF::new(bounds.origin(), bounds.size()),
660 background: Some(layout.background_color),
661 border: Default::default(),
662 corner_radius: 0.,
663 });
664
665 for rect in &layout.rects {
666 rect.paint(origin, &layout, cx)
667 }
668 });
669
670 //Draw Selection
671 cx.paint_layer(clip_bounds, |cx| {
672 let start_y = layout.highlights.get(0).map(|highlight| {
673 origin.y() + highlight.line_index as f32 * layout.size.line_height
674 });
675
676 if let Some(y) = start_y {
677 let range_lines = layout
678 .highlights
679 .iter()
680 .map(|relative_highlight| {
681 relative_highlight.to_highlighted_range_line(origin, layout)
682 })
683 .collect::<Vec<HighlightedRangeLine>>();
684
685 let hr = HighlightedRange {
686 start_y: y, //Need to change this
687 line_height: layout.size.line_height,
688 lines: range_lines,
689 color: layout.selection_color,
690 //Copied from editor. TODO: move to theme or something
691 corner_radius: 0.15 * layout.size.line_height,
692 };
693 hr.paint(bounds, cx.scene);
694 }
695 });
696
697 //Draw the text cells
698 cx.paint_layer(clip_bounds, |cx| {
699 for cell in &layout.cells {
700 cell.paint(origin, layout, visible_bounds, cx);
701 }
702 });
703
704 //Draw cursor
705 if let Some(cursor) = &layout.cursor {
706 cx.paint_layer(clip_bounds, |cx| {
707 cursor.paint(origin, cx);
708 })
709 }
710 });
711 }
712
713 fn dispatch_event(
714 &mut self,
715 event: &gpui::Event,
716 _bounds: gpui::geometry::rect::RectF,
717 visible_bounds: gpui::geometry::rect::RectF,
718 layout: &mut Self::LayoutState,
719 _paint: &mut Self::PaintState,
720 cx: &mut gpui::EventContext,
721 ) -> bool {
722 match event {
723 Event::ScrollWheel(ScrollWheelEvent {
724 delta, position, ..
725 }) => visible_bounds
726 .contains_point(*position)
727 .then(|| {
728 let vertical_scroll =
729 (delta.y() / layout.size.line_height) * ALACRITTY_SCROLL_MULTIPLIER;
730
731 self.terminal.upgrade(cx.app).map(|terminal| {
732 terminal.update(cx.app, |term, _| {
733 term.scroll(Scroll::Delta(vertical_scroll.round() as i32))
734 });
735 });
736 })
737 .is_some(),
738 Event::KeyDown(KeyDownEvent { keystroke, .. }) => {
739 if !cx.is_parent_view_focused() {
740 return false;
741 }
742
743 //TODO Talk to keith about how to catch events emitted from an element.
744 if let Some(view) = self.view.upgrade(cx.app) {
745 view.update(cx.app, |view, cx| view.clear_bel(cx))
746 }
747
748 self.terminal
749 .upgrade(cx.app)
750 .map(|model_handle| {
751 model_handle.update(cx.app, |term, _| term.try_keystroke(keystroke))
752 })
753 .unwrap_or(false)
754 }
755 _ => false,
756 }
757 }
758
759 fn metadata(&self) -> Option<&dyn std::any::Any> {
760 None
761 }
762
763 fn debug(
764 &self,
765 _bounds: gpui::geometry::rect::RectF,
766 _layout: &Self::LayoutState,
767 _paint: &Self::PaintState,
768 _cx: &gpui::DebugContext,
769 ) -> gpui::serde_json::Value {
770 json!({
771 "type": "TerminalElement",
772 })
773 }
774
775 fn rect_for_text_range(
776 &self,
777 _: Range<usize>,
778 bounds: RectF,
779 _: RectF,
780 layout: &Self::LayoutState,
781 _: &Self::PaintState,
782 _: &gpui::MeasurementContext,
783 ) -> Option<RectF> {
784 // Use the same origin that's passed to `Cursor::paint` in the paint
785 // method bove.
786 let mut origin = bounds.origin() + vec2f(layout.size.cell_width, 0.);
787
788 // TODO - Why is it necessary to move downward one line to get correct
789 // positioning? I would think that we'd want the same rect that is
790 // painted for the cursor.
791 origin += vec2f(0., layout.size.line_height);
792
793 Some(layout.cursor.as_ref()?.bounding_rect(origin))
794 }
795}
796
797mod test {
798
799 #[test]
800 fn test_mouse_to_selection() {
801 let term_width = 100.;
802 let term_height = 200.;
803 let cell_width = 10.;
804 let line_height = 20.;
805 let mouse_pos_x = 100.; //Window relative
806 let mouse_pos_y = 100.; //Window relative
807 let origin_x = 10.;
808 let origin_y = 20.;
809
810 let cur_size = crate::connected_el::TerminalSize::new(
811 line_height,
812 cell_width,
813 gpui::geometry::vector::vec2f(term_width, term_height),
814 );
815
816 let mouse_pos = gpui::geometry::vector::vec2f(mouse_pos_x, mouse_pos_y);
817 let origin = gpui::geometry::vector::vec2f(origin_x, origin_y); //Position of terminal window, 1 'cell' in
818 let (point, _) =
819 crate::connected_el::TerminalEl::mouse_to_cell_data(mouse_pos, origin, cur_size, 0);
820 assert_eq!(
821 point,
822 alacritty_terminal::index::Point::new(
823 alacritty_terminal::index::Line(((mouse_pos_y - origin_y) / line_height) as i32),
824 alacritty_terminal::index::Column(((mouse_pos_x - origin_x) / cell_width) as usize),
825 )
826 );
827 }
828}