use crate::cell_renderer::{CellRenderer, CellRendererConfig};
use crate::custom_shader_renderer::CustomShaderRenderer;
use crate::graphics_renderer::GraphicsRenderer;
use anyhow::Result;
use winit::dpi::PhysicalSize;
mod egui_render;
pub mod graphics;
mod layout;
pub mod params;
mod render_passes;
mod rendering;
mod resize;
mod screenshot;
pub mod shaders;
mod state;
mod types;
pub use par_term_config::SeparatorMark;
pub use params::RendererParams;
pub use rendering::{PaneCaptureParams, SplitPanesRenderParams};
pub use types::{
DividerRenderInfo, PaneDividerSettings, PaneRenderInfo, PaneTitleInfo,
compute_visible_separator_marks,
};
pub(crate) use types::fill_visible_separator_marks;
pub struct Renderer {
pub(crate) cell_renderer: CellRenderer,
pub(crate) graphics_renderer: GraphicsRenderer,
pub(crate) sixel_graphics: Vec<crate::graphics_renderer::GraphicRenderInfo>,
pub(crate) egui_renderer: egui_wgpu::Renderer,
pub(crate) custom_shader_renderer: Option<CustomShaderRenderer>,
pub(crate) custom_shader_path: Option<String>,
pub(crate) startup_shader_error: Option<String>,
pub(crate) cursor_shader_renderer: Option<CustomShaderRenderer>,
pub(crate) cursor_shader_path: Option<String>,
pub(crate) startup_cursor_shader_error: Option<String>,
pub(crate) size: PhysicalSize<u32>,
pub(crate) dirty: bool,
pub(crate) last_scrollbar_state: (usize, usize, usize, usize, u32, u32, u32, u32, u32, u32),
pub(crate) cursor_shader_disabled_for_alt_screen: bool,
pub(crate) debug_text: Option<String>,
pub(crate) scratch_divider_instances: Vec<crate::cell_renderer::BackgroundInstance>,
}
impl Renderer {
pub async fn new(params: RendererParams<'_>) -> Result<Self> {
let window = params.window;
let font_family = params.font_family;
let font_family_bold = params.font_family_bold;
let font_family_italic = params.font_family_italic;
let font_family_bold_italic = params.font_family_bold_italic;
let font_ranges = params.font_ranges;
let font_size = params.font_size;
let line_spacing = params.line_spacing;
let char_spacing = params.char_spacing;
let scrollbar_position = params.scrollbar_position;
let scrollbar_thumb_color = params.scrollbar_thumb_color;
let scrollbar_track_color = params.scrollbar_track_color;
let enable_text_shaping = params.enable_text_shaping;
let enable_ligatures = params.enable_ligatures;
let enable_kerning = params.enable_kerning;
let font_antialias = params.font_antialias;
let font_hinting = params.font_hinting;
let font_thin_strokes = params.font_thin_strokes;
let minimum_contrast = params.minimum_contrast;
let vsync_mode = params.vsync_mode;
let power_preference = params.power_preference;
let window_opacity = params.window_opacity;
let background_color = params.background_color;
let background_image_path = params.background_image_path;
let background_image_enabled = params.background_image_enabled;
let background_image_mode = params.background_image_mode;
let background_image_opacity = params.background_image_opacity;
let custom_shader_path = params.custom_shader_path;
let custom_shader_enabled = params.custom_shader_enabled;
let custom_shader_animation = params.custom_shader_animation;
let custom_shader_animation_speed = params.custom_shader_animation_speed;
let custom_shader_full_content = params.custom_shader_full_content;
let custom_shader_brightness = params.custom_shader_brightness;
let custom_shader_channel_paths = params.custom_shader_channel_paths;
let custom_shader_cubemap_path = params.custom_shader_cubemap_path;
let custom_shader_custom_uniforms = params.custom_shader_custom_uniforms;
let use_background_as_channel0 = params.use_background_as_channel0;
let background_channel0_blend_mode = params.background_channel0_blend_mode;
let custom_shader_auto_dim_under_text = params.custom_shader_auto_dim_under_text;
let custom_shader_auto_dim_strength = params.custom_shader_auto_dim_strength;
let image_scaling_mode = params.image_scaling_mode;
let image_preserve_aspect_ratio = params.image_preserve_aspect_ratio;
let cursor_shader_path = params.cursor_shader_path;
let cursor_shader_enabled = params.cursor_shader_enabled;
let cursor_shader_animation = params.cursor_shader_animation;
let cursor_shader_animation_speed = params.cursor_shader_animation_speed;
let size = window.inner_size();
let scale_factor = window.scale_factor();
let platform_dpi = if cfg!(target_os = "macos") {
72.0
} else {
96.0
};
let base_font_pixels = font_size * platform_dpi / 72.0;
let font_size_pixels = (base_font_pixels * scale_factor as f32).max(1.0);
let font_manager = par_term_fonts::font_manager::FontManager::new(
font_family,
font_family_bold,
font_family_italic,
font_family_bold_italic,
font_ranges,
)?;
let (font_ascent, font_descent, font_leading, char_advance) = {
let primary_font = font_manager
.get_font(0)
.expect("Primary font at index 0 must exist after FontManager initialization");
let metrics = primary_font.metrics(&[]);
let scale = font_size_pixels / metrics.units_per_em as f32;
let glyph_id = primary_font.charmap().map('m');
let advance = primary_font.glyph_metrics(&[]).advance_width(glyph_id) * scale;
(
metrics.ascent * scale,
metrics.descent * scale,
metrics.leading * scale,
advance,
)
};
let natural_line_height = font_ascent + font_descent + font_leading;
let char_height = (natural_line_height * line_spacing).max(1.0).round();
let scale = scale_factor as f32;
let window_padding = params.window_padding * scale;
let scrollbar_width = params.scrollbar_width * scale;
let available_width = (size.width as f32 - window_padding * 2.0 - scrollbar_width).max(0.0);
let available_height = (size.height as f32 - window_padding * 2.0).max(0.0);
let char_width = (char_advance * char_spacing).max(1.0).round(); let cols = (available_width / char_width).max(1.0) as usize;
let rows = (available_height / char_height).max(1.0) as usize;
let bg_path = if background_image_enabled {
background_image_path
} else {
None
};
log::info!(
"Renderer::new: background_image_enabled={}, path={:?}",
background_image_enabled,
bg_path
);
let cell_renderer = CellRenderer::new(
window.clone(),
CellRendererConfig {
font_manager,
font_size,
cols,
rows,
window_padding,
line_spacing,
char_spacing,
scrollbar_position,
scrollbar_width,
scrollbar_thumb_color,
scrollbar_track_color,
enable_text_shaping,
enable_ligatures,
enable_kerning,
font_antialias,
font_hinting,
font_thin_strokes,
minimum_contrast,
vsync_mode,
power_preference,
window_opacity,
background_color,
background_image_path: bg_path,
background_image_mode,
background_image_opacity,
},
)
.await?;
let egui_renderer = egui_wgpu::Renderer::new(
cell_renderer.device(),
cell_renderer.surface_format(),
egui_wgpu::RendererOptions {
msaa_samples: 1,
depth_stencil_format: None,
dithering: false,
predictable_texture_filtering: false,
},
);
let graphics_renderer = GraphicsRenderer::new(
cell_renderer.device(),
cell_renderer.surface_format(),
cell_renderer.cell_width(),
cell_renderer.cell_height(),
cell_renderer.window_padding(),
image_scaling_mode,
image_preserve_aspect_ratio,
)?;
let (mut custom_shader_renderer, initial_shader_path, startup_shader_error) =
shaders::init_custom_shader(
&cell_renderer,
shaders::CustomShaderInitParams {
size_width: size.width,
size_height: size.height,
window_padding,
path: custom_shader_path,
enabled: custom_shader_enabled,
animation: custom_shader_animation,
animation_speed: custom_shader_animation_speed,
window_opacity,
full_content: custom_shader_full_content,
brightness: custom_shader_brightness,
channel_paths: custom_shader_channel_paths,
cubemap_path: custom_shader_cubemap_path,
custom_uniforms: custom_shader_custom_uniforms,
use_background_as_channel0,
background_channel0_blend_mode,
auto_dim_under_text: custom_shader_auto_dim_under_text,
auto_dim_strength: custom_shader_auto_dim_strength,
},
);
let (mut cursor_shader_renderer, initial_cursor_shader_path, startup_cursor_shader_error) =
shaders::init_cursor_shader(
&cell_renderer,
shaders::CursorShaderInitParams {
size_width: size.width,
size_height: size.height,
window_padding,
path: cursor_shader_path,
enabled: cursor_shader_enabled,
animation: cursor_shader_animation,
animation_speed: cursor_shader_animation_speed,
window_opacity,
},
);
if let Some(ref mut cs) = custom_shader_renderer {
cs.set_scale_factor(scale);
}
if let Some(ref mut cs) = cursor_shader_renderer {
cs.set_scale_factor(scale);
}
log::info!(
"[renderer] Renderer created: custom_shader_loaded={}, cursor_shader_loaded={}",
initial_shader_path.is_some(),
initial_cursor_shader_path.is_some()
);
Ok(Self {
cell_renderer,
graphics_renderer,
sixel_graphics: Vec::new(),
egui_renderer,
custom_shader_renderer,
custom_shader_path: initial_shader_path,
startup_shader_error,
cursor_shader_renderer,
cursor_shader_path: initial_cursor_shader_path,
startup_cursor_shader_error,
size,
dirty: true, last_scrollbar_state: (usize::MAX, 0, 0, 0, 0, 0, 0, 0, 0, 0), cursor_shader_disabled_for_alt_screen: false,
debug_text: None,
scratch_divider_instances: Vec::new(),
})
}
}