1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
//! Cursor overlay instance generation for pane rendering.
//!
//! Provides [`CellRenderer::emit_cursor_overlays`] which appends background instances
//! for cursor-related overlays (guide, shadow, beam/underline bar, hollow outline) to
//! `self.bg_instances` starting at `bg_index` and returns the updated index.
use super::super::instance_buffers::HOLLOW_CURSOR_BORDER_PX;
use super::super::{BackgroundInstance, CellRenderer};
/// Parameters for [`CellRenderer::emit_cursor_overlays`].
pub(super) struct CursorOverlayParams {
/// Pixel X of the left edge of the cursor cell.
pub cursor_x0: f32,
/// Pixel X of the right edge of the cursor cell.
pub cursor_x1: f32,
/// Pixel Y of the top edge of the cursor cell.
pub cursor_y0: f32,
/// Pixel Y of the bottom edge of the cursor cell.
pub cursor_y1: f32,
/// Number of visible columns in the pane (for guide line width).
pub cols: usize,
/// Pixel X of the content area origin (left edge after padding).
pub content_x: f32,
/// Current blink opacity (0.0 = invisible, 1.0 = fully visible).
pub cursor_opacity: f32,
}
impl CellRenderer {
/// Append cursor overlay instances (guide, shadow, beam/underline bar, hollow outline)
/// to `self.bg_instances` starting at `bg_index`.
///
/// Returns the updated `bg_index` after all overlays have been appended.
pub(super) fn emit_cursor_overlays(
&mut self,
p: CursorOverlayParams,
mut bg_index: usize,
) -> usize {
let CursorOverlayParams {
cursor_x0,
cursor_x1,
cursor_y0,
cursor_y1,
cols,
content_x,
cursor_opacity,
} = p;
let w = self.config.width as f32;
let h = self.config.height as f32;
// Cursor guide (horizontal line spanning viewport width at cursor row)
if cursor_opacity > 0.0
&& !self.cursor.hidden_for_shader
&& self.cursor.guide_enabled
&& bg_index < self.buffers.max_bg_instances
{
let guide_x0 = content_x;
let guide_x1 = content_x + cols as f32 * self.grid.cell_width;
self.bg_instances[bg_index] = BackgroundInstance {
position: [guide_x0 / w * 2.0 - 1.0, 1.0 - (cursor_y0 / h * 2.0)],
size: [
(guide_x1 - guide_x0) / w * 2.0,
(cursor_y1 - cursor_y0) / h * 2.0,
],
color: self.cursor.guide_color,
};
bg_index += 1;
}
// Cursor shadow (offset rectangle behind cursor)
if cursor_opacity > 0.0
&& !self.cursor.hidden_for_shader
&& self.cursor.shadow_enabled
&& bg_index < self.buffers.max_bg_instances
{
let shadow_x0 = cursor_x0 + self.cursor.shadow_offset[0];
let shadow_y0 = cursor_y0 + self.cursor.shadow_offset[1];
self.bg_instances[bg_index] = BackgroundInstance {
position: [shadow_x0 / w * 2.0 - 1.0, 1.0 - (shadow_y0 / h * 2.0)],
size: [
self.grid.cell_width / w * 2.0,
self.grid.cell_height / h * 2.0,
],
color: self.cursor.shadow_color,
};
bg_index += 1;
}
// Beam or underline cursor bar (on top of text)
if cursor_opacity > 0.0 && !self.cursor.hidden_for_shader {
use par_term_emu_core_rust::cursor::CursorStyle;
let cc = self.cursor.color;
let overlay = match self.cursor.style {
CursorStyle::SteadyBar | CursorStyle::BlinkingBar => Some(BackgroundInstance {
position: [cursor_x0 / w * 2.0 - 1.0, 1.0 - (cursor_y0 / h * 2.0)],
size: [2.0 / w * 2.0, (cursor_y1 - cursor_y0) / h * 2.0],
color: [cc[0], cc[1], cc[2], cursor_opacity],
}),
CursorStyle::SteadyUnderline | CursorStyle::BlinkingUnderline => {
Some(BackgroundInstance {
position: [
cursor_x0 / w * 2.0 - 1.0,
1.0 - ((cursor_y1 - 2.0) / h * 2.0),
],
size: [(cursor_x1 - cursor_x0) / w * 2.0, 2.0 / h * 2.0],
color: [cc[0], cc[1], cc[2], cursor_opacity],
})
}
_ => None,
};
if let Some(inst) = overlay
&& bg_index < self.buffers.max_bg_instances
{
self.bg_instances[bg_index] = inst;
bg_index += 1;
}
}
// Hollow cursor outline (4 borders) — independent of blink opacity
let render_hollow = !self.cursor.hidden_for_shader
&& !self.is_focused
&& self.cursor.unfocused_style == par_term_config::UnfocusedCursorStyle::Hollow;
if render_hollow {
let border_width = HOLLOW_CURSOR_BORDER_PX;
let color = [
self.cursor.color[0],
self.cursor.color[1],
self.cursor.color[2],
1.0, // Always fully opaque regardless of blink phase
];
let cell_w = (cursor_x1 - cursor_x0) / w * 2.0;
let cell_h = (cursor_y1 - cursor_y0) / h * 2.0;
let bw = border_width / w * 2.0;
let bh = border_width / h * 2.0;
let cx = cursor_x0 / w * 2.0 - 1.0;
let cy = 1.0 - (cursor_y0 / h * 2.0);
let borders = [
// Top
BackgroundInstance {
position: [cx, cy],
size: [cell_w, bh],
color,
},
// Bottom
BackgroundInstance {
position: [cx, 1.0 - ((cursor_y1 - border_width) / h * 2.0)],
size: [cell_w, bh],
color,
},
// Left
BackgroundInstance {
position: [cx, 1.0 - ((cursor_y0 + border_width) / h * 2.0)],
size: [bw, cell_h - bh * 2.0],
color,
},
// Right
BackgroundInstance {
position: [
(cursor_x1 - border_width) / w * 2.0 - 1.0,
1.0 - ((cursor_y0 + border_width) / h * 2.0),
],
size: [bw, cell_h - bh * 2.0],
color,
},
];
for border in borders {
if bg_index < self.buffers.max_bg_instances {
self.bg_instances[bg_index] = border;
bg_index += 1;
}
}
}
bg_index
}
}