use super::prettifier_cells;
use super::types::RendererSizing;
use crate::config::color_u8_to_f32_a;
use crate::progress_bar::ProgressBarSnapshot;
use crate::renderer::Renderer;
use crate::ui_constants::{SCROLLBAR_MARK_HIT_RADIUS_PX, VISUAL_BELL_FLASH_DURATION_MS};
pub(super) struct GpuStateUpdateParams<'a> {
pub(super) tab_manager: &'a mut crate::tab::TabManager,
pub(super) config: &'a crate::config::Config,
pub(super) cursor_anim: &'a crate::app::window_state::cursor_anim_state::CursorAnimState,
pub(super) window: &'a Option<std::sync::Arc<winit::window::Window>>,
pub(super) debug: &'a crate::app::window_state::debug_state::DebugState,
pub(super) cells: &'a [crate::cell_renderer::Cell],
pub(super) current_cursor_pos: Option<(usize, usize)>,
pub(super) cursor_style: Option<par_term_emu_core_rust::cursor::CursorStyle>,
pub(super) progress_snapshot: &'a Option<ProgressBarSnapshot>,
pub(super) prettifier_graphics: &'a [prettifier_cells::PrettifierGraphic],
pub(super) scroll_offset: usize,
pub(super) visible_lines: usize,
pub(super) scrollback_len: usize,
pub(super) total_lines: usize,
pub(super) is_alt_screen: bool,
pub(super) scrollback_marks: &'a [crate::scrollback_metadata::ScrollbackMark],
pub(super) status_bar_height: f32,
pub(super) custom_status_bar_height: f32,
}
pub(super) struct GpuUploadResult {
pub(super) debug_update_cells_time: std::time::Duration,
pub(super) debug_graphics_time: std::time::Duration,
pub(super) debug_anim_time: std::time::Duration,
pub(super) sizing: RendererSizing,
pub(super) hovered_mark: Option<crate::scrollback_metadata::ScrollbackMark>,
}
pub(super) fn update_gpu_renderer_state(
renderer: &mut Renderer,
p: GpuStateUpdateParams<'_>,
) -> GpuUploadResult {
let GpuStateUpdateParams {
tab_manager,
config,
cursor_anim,
window,
debug,
cells,
current_cursor_pos,
cursor_style,
progress_snapshot,
prettifier_graphics,
scroll_offset,
visible_lines,
scrollback_len,
total_lines,
is_alt_screen,
scrollback_marks,
status_bar_height,
custom_status_bar_height,
} = p;
let mut debug_update_cells_time = std::time::Duration::ZERO;
#[allow(unused_assignments)]
let mut debug_graphics_time = std::time::Duration::ZERO;
let disable_cursor_shader = config.shader.cursor_shader_disable_in_alt_screen && is_alt_screen;
renderer.set_cursor_shader_disabled_for_alt_screen(disable_cursor_shader);
if !debug.cache_hit {
let t = std::time::Instant::now();
renderer.update_cells(cells);
debug_update_cells_time = t.elapsed();
}
if let (Some(pos), Some(opacity), Some(style)) = (
current_cursor_pos,
Some(cursor_anim.cursor_opacity),
cursor_style,
) {
renderer.update_cursor(pos, opacity, style);
let cursor_color = color_u8_to_f32_a(config.cursor_color, 1.0);
renderer.update_shader_cursor(pos.0, pos.1, opacity, cursor_color, style);
} else {
renderer.clear_cursor();
}
if let Some(snap) = progress_snapshot {
use par_term_emu_core_rust::terminal::ProgressState;
let state_val = match snap.simple.state {
ProgressState::Hidden => 0.0,
ProgressState::Normal => 1.0,
ProgressState::Error => 2.0,
ProgressState::Indeterminate => 3.0,
ProgressState::Warning => 4.0,
};
let active_count = (if snap.simple.is_active() { 1 } else { 0 })
+ snap.named.values().filter(|b| b.state.is_active()).count();
renderer.update_shader_progress(
state_val,
snap.simple.progress as f32 / 100.0,
if snap.has_active() { 1.0 } else { 0.0 },
active_count as f32,
);
} else {
renderer.update_shader_progress(0.0, 0.0, 0.0, 0.0);
}
renderer.update_scrollbar(scroll_offset, visible_lines, total_lines, scrollback_marks);
if config.command_separator_enabled {
let separator_marks = crate::renderer::compute_visible_separator_marks(
scrollback_marks,
scrollback_len,
scroll_offset,
visible_lines,
);
renderer.set_separator_marks(separator_marks);
} else {
renderer.set_separator_marks(Vec::new());
}
{
let gutter_data = if let Some(tab) = tab_manager.active_tab() {
if let Some(ref pipeline) = tab.prettifier {
if pipeline.is_enabled() {
let indicators = tab.gutter_manager.indicators_for_viewport(
pipeline,
scroll_offset,
visible_lines,
);
let gutter_color = [0.3, 0.5, 0.8, 0.15];
indicators
.iter()
.flat_map(|ind| {
(ind.row..ind.row + ind.height).map(move |r| (r, gutter_color))
})
.collect::<Vec<_>>()
} else {
Vec::new()
}
} else {
Vec::new()
}
} else {
Vec::new()
};
renderer.set_gutter_indicators(gutter_data);
}
let anim_start = std::time::Instant::now();
if let Some(tab) = tab_manager.active_tab()
&& let Ok(terminal) = tab.terminal.try_write()
&& terminal.update_animations()
{
if let Some(w) = window {
w.request_redraw();
}
}
let debug_anim_time = anim_start.elapsed();
let graphics_start = std::time::Instant::now();
let has_pane_manager_for_graphics = tab_manager
.active_tab()
.and_then(|t| t.pane_manager.as_ref())
.map(|pm| pm.pane_count() > 0)
.unwrap_or(false);
if !has_pane_manager_for_graphics
&& let Some(tab) = tab_manager.active_tab()
&& let Ok(terminal) = tab.terminal.try_write()
{
terminal.invalidate_overwritten_graphics();
let mut graphics = terminal.get_graphics_with_animations();
let scrollback_len_for_gfx = terminal.scrollback_len();
let scrollback_graphics = terminal.get_scrollback_graphics();
let scrollback_count = scrollback_graphics.len();
graphics.extend(scrollback_graphics);
crate::debug_info!(
"APP",
"Got {} graphics ({} scrollback) from terminal (scroll_offset={}, scrollback_len={})",
graphics.len(),
scrollback_count,
scroll_offset,
scrollback_len_for_gfx
);
if let Err(e) = renderer.update_graphics(
&graphics,
scroll_offset,
scrollback_len_for_gfx,
visible_lines,
) {
log::error!("Failed to update graphics: {}", e);
}
}
debug_graphics_time = graphics_start.elapsed();
if !prettifier_graphics.is_empty() {
let refs: Vec<par_term_render::renderer::graphics::PrettifierGraphicRef<'_>> =
prettifier_graphics
.iter()
.map(|(id, data, w, h, row, col)| (*id, data.as_slice(), *w, *h, *row, *col))
.collect();
if let Err(e) = renderer.update_prettifier_graphics(&refs) {
crate::debug_error!("PRETTIFIER", "Failed to upload prettifier graphics: {}", e);
}
}
let visual_bell_flash = tab_manager
.active_tab()
.and_then(|t| t.active_bell().visual_flash);
let visual_bell_intensity = if let Some(flash_start) = visual_bell_flash {
let elapsed = flash_start.elapsed().as_millis();
if elapsed < VISUAL_BELL_FLASH_DURATION_MS {
if let Some(w) = window {
w.request_redraw();
}
0.3 * (1.0 - (elapsed as f32 / VISUAL_BELL_FLASH_DURATION_MS as f32))
} else {
0.0
}
} else {
0.0
};
let visual_bell_color_f32: [f32; 3] = [
config.notifications.notification_visual_bell_color[0] as f32 / 255.0,
config.notifications.notification_visual_bell_color[1] as f32 / 255.0,
config.notifications.notification_visual_bell_color[2] as f32 / 255.0,
];
renderer.set_visual_bell_color(visual_bell_color_f32);
renderer.set_visual_bell_intensity(visual_bell_intensity);
let hovered_mark: Option<crate::scrollback_metadata::ScrollbackMark> =
if config.scrollbar_mark_tooltips && config.scrollbar_command_marks {
tab_manager
.active_tab()
.map(|tab| tab.active_mouse().position)
.and_then(|(mx, my)| {
renderer.scrollbar_mark_at_position(
mx as f32,
my as f32,
SCROLLBAR_MARK_HIT_RADIUS_PX,
)
})
.cloned()
} else {
None
};
let sizing = RendererSizing {
size: renderer.size(),
content_offset_y: renderer.content_offset_y(),
content_offset_x: renderer.content_offset_x(),
content_inset_bottom: renderer.content_inset_bottom(),
content_inset_right: renderer.content_inset_right(),
cell_width: renderer.cell_width(),
cell_height: renderer.cell_height(),
padding: renderer.window_padding(),
status_bar_height: (status_bar_height + custom_status_bar_height) * renderer.scale_factor(),
scale_factor: renderer.scale_factor(),
scrollbar_width: renderer.scrollbar_width(),
};
GpuUploadResult {
debug_update_cells_time,
debug_graphics_time,
debug_anim_time,
sizing,
hovered_mark,
}
}