use std::fmt::Debug;
use super::{FlushKind, RenderOp, RenderOpsLocalData, RenderPipeline};
use crate::{diff_chunks::PixelCharDiffChunks, GlobalData, LockedOutputDevice,
OffscreenBuffer, OffscreenBufferPaint, OffscreenBufferPaintImplCrossterm,
Pos, Size, TerminalLibBackend, DEBUG_TUI_COMPOSITOR,
DEBUG_TUI_SHOW_PIPELINE_EXPANDED, TERMINAL_LIB_BACKEND};
pub trait PaintRenderOp {
fn paint(
&mut self,
skip_flush: &mut bool,
render_op: &RenderOp,
window_size: Size,
local_data: &mut RenderOpsLocalData,
locked_output_device: LockedOutputDevice<'_>,
is_mock: bool,
);
}
pub fn paint<S, AS>(
pipeline: &RenderPipeline,
flush_kind: FlushKind,
global_data: &mut GlobalData<S, AS>,
locked_output_device: LockedOutputDevice<'_>,
is_mock: bool,
) where
S: Debug + Default + Clone + Sync + Send,
AS: Debug + Default + Clone + Sync + Send,
{
fn perform_diff_paint(
diff_chunks: &PixelCharDiffChunks,
window_size: Size,
locked_output_device: LockedOutputDevice<'_>,
is_mock: bool,
) {
match TERMINAL_LIB_BACKEND {
TerminalLibBackend::Crossterm => {
let mut crossterm_impl = OffscreenBufferPaintImplCrossterm {};
let render_ops = crossterm_impl.render_diff(diff_chunks);
crossterm_impl.paint_diff(
render_ops,
window_size,
locked_output_device,
is_mock,
);
}
TerminalLibBackend::Termion => unimplemented!(),
}
}
fn perform_full_paint(
offscreen_buffer: &OffscreenBuffer,
flush_kind: FlushKind,
window_size: Size,
locked_output_device: LockedOutputDevice<'_>,
is_mock: bool,
) {
match TERMINAL_LIB_BACKEND {
TerminalLibBackend::Crossterm => {
let mut crossterm_impl = OffscreenBufferPaintImplCrossterm {};
let render_ops = crossterm_impl.render(offscreen_buffer);
crossterm_impl.paint(
render_ops,
flush_kind,
window_size,
locked_output_device,
is_mock,
);
}
TerminalLibBackend::Termion => unimplemented!(),
}
}
let maybe_saved_offscreen_buffer = global_data.maybe_saved_offscreen_buffer.clone();
let window_size = global_data.window_size;
let Some(mut buffer_from_pool) = global_data.offscreen_buffer_pool.take() else {
panic!("All offscreen buffers are currently taken. This should never happen.");
};
pipeline.convert(window_size, &mut buffer_from_pool);
match maybe_saved_offscreen_buffer {
None => {
perform_full_paint(
&buffer_from_pool,
flush_kind,
window_size,
locked_output_device,
is_mock,
);
}
Some(saved_offscreen_buffer) => {
match saved_offscreen_buffer.diff(&buffer_from_pool) {
None => {
perform_full_paint(
&buffer_from_pool,
flush_kind,
window_size,
locked_output_device,
is_mock,
);
}
Some(ref diff_chunks) => {
perform_diff_paint(
diff_chunks,
window_size,
locked_output_device,
is_mock,
);
}
}
}
}
if let Some(old_buffer) = global_data.maybe_saved_offscreen_buffer.take() {
global_data.offscreen_buffer_pool.give_back(old_buffer);
}
global_data.maybe_saved_offscreen_buffer = Some(buffer_from_pool);
}
pub fn sanitize_and_save_abs_pos(
orig_abs_pos: Pos,
window_size: Size,
local_data: &mut RenderOpsLocalData,
) -> Pos {
let Size {
col_width: window_width,
row_height: window_height,
} = window_size;
let mut sanitized_abs_pos = orig_abs_pos;
sanitized_abs_pos.col_index = sanitized_abs_pos
.col_index
.min(window_width.convert_to_col_index());
sanitized_abs_pos.row_index = sanitized_abs_pos
.row_index
.min(window_height.convert_to_row_index());
local_data.cursor_pos = sanitized_abs_pos;
debug(orig_abs_pos, sanitized_abs_pos);
sanitized_abs_pos
}
fn debug(orig_pos: Pos, sanitized_pos: Pos) {
DEBUG_TUI_COMPOSITOR.then(|| {
if sanitized_pos != orig_pos {
tracing::info!(
message = "pipeline : ⮻ Attempt to set cursor position (orig) outside of terminal window; clamping to nearest edge of window (sanitized)",
orig = ?orig_pos,
sanitized = ?sanitized_pos
);
}
});
DEBUG_TUI_SHOW_PIPELINE_EXPANDED.then(|| {
tracing::info!(
message = "pipeline : ⮺ Save the cursor position (sanitized) to SharedGlobalData",
sanitized = ?sanitized_pos
);
});
}