par_term_render/renderer/
mod.rs1use crate::cell_renderer::{CellRenderer, CellRendererConfig};
15use crate::custom_shader_renderer::CustomShaderRenderer;
16use crate::graphics_renderer::GraphicsRenderer;
17use anyhow::Result;
18use winit::dpi::PhysicalSize;
19
20mod egui_render;
21pub mod graphics;
22mod layout;
23pub mod params;
24
25mod render_passes;
26mod rendering;
27mod resize;
28mod screenshot;
29pub mod shaders;
30mod state;
31mod types;
32
33pub use par_term_config::SeparatorMark;
35pub use params::RendererParams;
36pub use rendering::{PaneCaptureParams, SplitPanesRenderParams};
37pub use types::{
38 DividerRenderInfo, PaneDividerSettings, PaneRenderInfo, PaneTitleInfo,
39 compute_visible_separator_marks,
40};
41pub(crate) use types::fill_visible_separator_marks;
44
45pub struct Renderer {
47 pub(crate) cell_renderer: CellRenderer,
49
50 pub(crate) graphics_renderer: GraphicsRenderer,
52
53 pub(crate) sixel_graphics: Vec<crate::graphics_renderer::GraphicRenderInfo>,
56
57 pub(crate) egui_renderer: egui_wgpu::Renderer,
59
60 pub(crate) custom_shader_renderer: Option<CustomShaderRenderer>,
62 pub(crate) custom_shader_path: Option<String>,
64 pub(crate) startup_shader_error: Option<String>,
67
68 pub(crate) cursor_shader_renderer: Option<CustomShaderRenderer>,
70 pub(crate) cursor_shader_path: Option<String>,
72 pub(crate) startup_cursor_shader_error: Option<String>,
74
75 pub(crate) size: PhysicalSize<u32>,
77
78 pub(crate) dirty: bool,
80
81 pub(crate) last_scrollbar_state: (usize, usize, usize, usize, u32, u32, u32, u32, u32, u32),
85
86 pub(crate) cursor_shader_disabled_for_alt_screen: bool,
88
89 pub(crate) debug_text: Option<String>,
91
92 pub(crate) scratch_divider_instances: Vec<crate::cell_renderer::BackgroundInstance>,
95}
96
97impl Renderer {
98 pub async fn new(params: RendererParams<'_>) -> Result<Self> {
100 let window = params.window;
101 let font_family = params.font_family;
102 let font_family_bold = params.font_family_bold;
103 let font_family_italic = params.font_family_italic;
104 let font_family_bold_italic = params.font_family_bold_italic;
105 let font_ranges = params.font_ranges;
106 let font_size = params.font_size;
107 let line_spacing = params.line_spacing;
108 let char_spacing = params.char_spacing;
109 let scrollbar_position = params.scrollbar_position;
110 let scrollbar_thumb_color = params.scrollbar_thumb_color;
111 let scrollbar_track_color = params.scrollbar_track_color;
112 let enable_text_shaping = params.enable_text_shaping;
113 let enable_ligatures = params.enable_ligatures;
114 let enable_kerning = params.enable_kerning;
115 let font_antialias = params.font_antialias;
116 let font_hinting = params.font_hinting;
117 let font_thin_strokes = params.font_thin_strokes;
118 let minimum_contrast = params.minimum_contrast;
119 let vsync_mode = params.vsync_mode;
120 let power_preference = params.power_preference;
121 let window_opacity = params.window_opacity;
122 let background_color = params.background_color;
123 let background_image_path = params.background_image_path;
124 let background_image_enabled = params.background_image_enabled;
125 let background_image_mode = params.background_image_mode;
126 let background_image_opacity = params.background_image_opacity;
127 let custom_shader_path = params.custom_shader_path;
128 let custom_shader_enabled = params.custom_shader_enabled;
129 let custom_shader_animation = params.custom_shader_animation;
130 let custom_shader_animation_speed = params.custom_shader_animation_speed;
131 let custom_shader_full_content = params.custom_shader_full_content;
132 let custom_shader_brightness = params.custom_shader_brightness;
133 let custom_shader_channel_paths = params.custom_shader_channel_paths;
134 let custom_shader_cubemap_path = params.custom_shader_cubemap_path;
135 let custom_shader_custom_uniforms = params.custom_shader_custom_uniforms;
136 let use_background_as_channel0 = params.use_background_as_channel0;
137 let background_channel0_blend_mode = params.background_channel0_blend_mode;
138 let custom_shader_auto_dim_under_text = params.custom_shader_auto_dim_under_text;
139 let custom_shader_auto_dim_strength = params.custom_shader_auto_dim_strength;
140 let image_scaling_mode = params.image_scaling_mode;
141 let image_preserve_aspect_ratio = params.image_preserve_aspect_ratio;
142 let cursor_shader_path = params.cursor_shader_path;
143 let cursor_shader_enabled = params.cursor_shader_enabled;
144 let cursor_shader_animation = params.cursor_shader_animation;
145 let cursor_shader_animation_speed = params.cursor_shader_animation_speed;
146
147 let size = window.inner_size();
148 let scale_factor = window.scale_factor();
149
150 let platform_dpi = if cfg!(target_os = "macos") {
153 72.0
154 } else {
155 96.0
156 };
157
158 let base_font_pixels = font_size * platform_dpi / 72.0;
160 let font_size_pixels = (base_font_pixels * scale_factor as f32).max(1.0);
161
162 let font_manager = par_term_fonts::font_manager::FontManager::new(
166 font_family,
167 font_family_bold,
168 font_family_italic,
169 font_family_bold_italic,
170 font_ranges,
171 )?;
172
173 let (font_ascent, font_descent, font_leading, char_advance) = {
174 let primary_font = font_manager
175 .get_font(0)
176 .expect("Primary font at index 0 must exist after FontManager initialization");
177 let metrics = primary_font.metrics(&[]);
178 let scale = font_size_pixels / metrics.units_per_em as f32;
179
180 let glyph_id = primary_font.charmap().map('m');
182 let advance = primary_font.glyph_metrics(&[]).advance_width(glyph_id) * scale;
183
184 (
185 metrics.ascent * scale,
186 metrics.descent * scale,
187 metrics.leading * scale,
188 advance,
189 )
190 };
191
192 let natural_line_height = font_ascent + font_descent + font_leading;
195 let char_height = (natural_line_height * line_spacing).max(1.0).round();
196
197 let scale = scale_factor as f32;
199 let window_padding = params.window_padding * scale;
200 let scrollbar_width = params.scrollbar_width * scale;
201
202 let available_width = (size.width as f32 - window_padding * 2.0 - scrollbar_width).max(0.0);
204 let available_height = (size.height as f32 - window_padding * 2.0).max(0.0);
205
206 let char_width = (char_advance * char_spacing).max(1.0).round(); let cols = (available_width / char_width).max(1.0) as usize;
209 let rows = (available_height / char_height).max(1.0) as usize;
210
211 let bg_path = if background_image_enabled {
213 background_image_path
214 } else {
215 None
216 };
217 log::info!(
218 "Renderer::new: background_image_enabled={}, path={:?}",
219 background_image_enabled,
220 bg_path
221 );
222 let cell_renderer = CellRenderer::new(
223 window.clone(),
224 CellRendererConfig {
225 font_manager,
226 font_size,
227 cols,
228 rows,
229 window_padding,
230 line_spacing,
231 char_spacing,
232 scrollbar_position,
233 scrollbar_width,
234 scrollbar_thumb_color,
235 scrollbar_track_color,
236 enable_text_shaping,
237 enable_ligatures,
238 enable_kerning,
239 font_antialias,
240 font_hinting,
241 font_thin_strokes,
242 minimum_contrast,
243 vsync_mode,
244 power_preference,
245 window_opacity,
246 background_color,
247 background_image_path: bg_path,
248 background_image_mode,
249 background_image_opacity,
250 },
251 )
252 .await?;
253
254 let egui_renderer = egui_wgpu::Renderer::new(
256 cell_renderer.device(),
257 cell_renderer.surface_format(),
258 egui_wgpu::RendererOptions {
259 msaa_samples: 1,
260 depth_stencil_format: None,
261 dithering: false,
262 predictable_texture_filtering: false,
263 },
264 );
265
266 let graphics_renderer = GraphicsRenderer::new(
268 cell_renderer.device(),
269 cell_renderer.surface_format(),
270 cell_renderer.cell_width(),
271 cell_renderer.cell_height(),
272 cell_renderer.window_padding(),
273 image_scaling_mode,
274 image_preserve_aspect_ratio,
275 )?;
276
277 let (mut custom_shader_renderer, initial_shader_path, startup_shader_error) =
279 shaders::init_custom_shader(
280 &cell_renderer,
281 shaders::CustomShaderInitParams {
282 size_width: size.width,
283 size_height: size.height,
284 window_padding,
285 path: custom_shader_path,
286 enabled: custom_shader_enabled,
287 animation: custom_shader_animation,
288 animation_speed: custom_shader_animation_speed,
289 window_opacity,
290 full_content: custom_shader_full_content,
291 brightness: custom_shader_brightness,
292 channel_paths: custom_shader_channel_paths,
293 cubemap_path: custom_shader_cubemap_path,
294 custom_uniforms: custom_shader_custom_uniforms,
295 use_background_as_channel0,
296 background_channel0_blend_mode,
297 auto_dim_under_text: custom_shader_auto_dim_under_text,
298 auto_dim_strength: custom_shader_auto_dim_strength,
299 },
300 );
301
302 let (mut cursor_shader_renderer, initial_cursor_shader_path, startup_cursor_shader_error) =
304 shaders::init_cursor_shader(
305 &cell_renderer,
306 shaders::CursorShaderInitParams {
307 size_width: size.width,
308 size_height: size.height,
309 window_padding,
310 path: cursor_shader_path,
311 enabled: cursor_shader_enabled,
312 animation: cursor_shader_animation,
313 animation_speed: cursor_shader_animation_speed,
314 window_opacity,
315 },
316 );
317
318 if let Some(ref mut cs) = custom_shader_renderer {
320 cs.set_scale_factor(scale);
321 }
322 if let Some(ref mut cs) = cursor_shader_renderer {
323 cs.set_scale_factor(scale);
324 }
325
326 log::info!(
327 "[renderer] Renderer created: custom_shader_loaded={}, cursor_shader_loaded={}",
328 initial_shader_path.is_some(),
329 initial_cursor_shader_path.is_some()
330 );
331
332 Ok(Self {
333 cell_renderer,
334 graphics_renderer,
335 sixel_graphics: Vec::new(),
336 egui_renderer,
337 custom_shader_renderer,
338 custom_shader_path: initial_shader_path,
339 startup_shader_error,
340 cursor_shader_renderer,
341 cursor_shader_path: initial_cursor_shader_path,
342 startup_cursor_shader_error,
343 size,
344 dirty: true, last_scrollbar_state: (usize::MAX, 0, 0, 0, 0, 0, 0, 0, 0, 0), cursor_shader_disabled_for_alt_screen: false,
347 debug_text: None,
348 scratch_divider_instances: Vec::new(),
349 })
350 }
351}