1use anyhow::{Context, Result};
14use std::collections::HashMap;
15use std::sync::Arc;
16use winit::window::Window;
17
18use crate::scrollbar::Scrollbar;
19use crate::wgpu_conversions::{PowerPreferenceWgpu, VsyncModeWgpu};
20use par_term_config::{SeparatorMark, color_u8_to_f32_a};
21use par_term_fonts::font_manager::FontManager;
22
23pub mod atlas;
24pub mod background;
25mod bg_instance_builder;
26pub mod block_chars;
27mod cursor;
28mod font;
29mod instance_buffers;
30mod layout;
31pub(crate) mod pane_render;
32pub mod pipeline;
33pub mod render;
34mod settings;
35pub mod surface;
36mod text_instance_builder;
37pub mod types;
38pub(crate) use pane_render::{PaneRenderViewParams, pane_instance_capacity};
40pub use types::{Cell, PaneViewport};
41pub(crate) use types::{BackgroundInstance, GlyphInfo, RowCacheEntry, TextInstance};
43pub(crate) use instance_buffers::{CURSOR_OVERLAY_SLOTS, TEXT_INSTANCES_PER_CELL};
45pub(crate) use cursor::CursorState;
47pub(crate) use font::FontState;
48pub(crate) use layout::GridLayout;
49
50pub(crate) const MACOS_PLATFORM_DPI: f32 = 72.0;
52
53pub(crate) const DEFAULT_PLATFORM_DPI: f32 = 96.0;
55
56pub(crate) const FONT_REFERENCE_DPI: f32 = 72.0;
59
60const SOLID_PIXEL_SIZE: u32 = 2;
63
64pub(crate) const ATLAS_GLYPH_PADDING: u32 = 2;
66
67const SURFACE_FRAME_LATENCY: u32 = 2;
71
72const DEFAULT_GUIDE_OPACITY: f32 = 0.08;
75
76const DEFAULT_SHADOW_ALPHA: f32 = 0.5;
78
79const DEFAULT_SHADOW_OFFSET_PX: f32 = 2.0;
81
82const DEFAULT_SHADOW_BLUR_PX: f32 = 3.0;
84
85pub(crate) struct GpuPipelines {
87 pub(crate) bg_pipeline: wgpu::RenderPipeline,
88 pub(crate) text_pipeline: wgpu::RenderPipeline,
89 pub(crate) bg_image_pipeline: wgpu::RenderPipeline,
90 pub(crate) visual_bell_pipeline: wgpu::RenderPipeline,
92 pub(crate) text_bind_group: wgpu::BindGroup,
93 #[allow(dead_code)] pub(crate) text_bind_group_layout: wgpu::BindGroupLayout,
95 pub(crate) bg_image_bind_group: Option<wgpu::BindGroup>,
96 pub(crate) bg_image_bind_group_layout: wgpu::BindGroupLayout,
97 pub(crate) visual_bell_bind_group: wgpu::BindGroup,
99 pub(crate) opaque_alpha_pipeline: wgpu::RenderPipeline,
101}
102
103pub(crate) struct GpuBuffers {
105 pub(crate) vertex_buffer: wgpu::Buffer,
106 pub(crate) bg_instance_buffer: wgpu::Buffer,
107 pub(crate) text_instance_buffer: wgpu::Buffer,
108 pub(crate) bg_image_uniform_buffer: wgpu::Buffer,
109 pub(crate) visual_bell_uniform_buffer: wgpu::Buffer,
111 pub(crate) max_bg_instances: usize,
113 pub(crate) max_text_instances: usize,
115 pub(crate) actual_bg_instances: usize,
117 pub(crate) actual_text_instances: usize,
119 pub(crate) pane_bg_cursor: usize,
125 pub(crate) pane_text_cursor: usize,
128 pub(crate) overflow_reported: bool,
131}
132
133pub(crate) struct GlyphAtlas {
135 pub(crate) atlas_texture: wgpu::Texture,
136 #[allow(dead_code)] pub(crate) atlas_view: wgpu::TextureView,
138 pub(crate) glyph_cache: HashMap<u64, GlyphInfo>,
139 pub(crate) lru_head: Option<u64>,
140 pub(crate) lru_tail: Option<u64>,
141 pub(crate) atlas_next_x: u32,
142 pub(crate) atlas_next_y: u32,
143 pub(crate) atlas_row_height: u32,
144 pub(crate) atlas_size: u32,
146 pub(crate) solid_pixel_offset: (u32, u32),
148}
149
150pub(crate) struct BackgroundImageState {
152 pub(crate) bg_image_texture: Option<wgpu::Texture>,
153 pub(crate) bg_image_mode: par_term_config::BackgroundImageMode,
154 pub(crate) bg_image_opacity: f32,
155 pub(crate) bg_image_width: u32,
156 pub(crate) bg_image_height: u32,
157 pub(crate) bg_is_solid_color: bool,
161 pub(crate) solid_bg_color: [f32; 3],
164 pub(crate) pane_bg_cache: HashMap<String, background::PaneBackgroundEntry>,
166 pub(crate) pane_bg_uniform_cache: HashMap<usize, background::PaneBgUniformEntry>,
175}
176
177pub(crate) struct SeparatorConfig {
179 pub(crate) enabled: bool,
181 pub(crate) thickness: f32,
183 pub(crate) opacity: f32,
185 pub(crate) exit_color: bool,
187 pub(crate) color: [f32; 3],
189 pub(crate) visible_marks: Vec<SeparatorMark>,
191}
192
193pub struct CellRenderer {
194 pub(crate) device: Arc<wgpu::Device>,
196 pub(crate) queue: Arc<wgpu::Queue>,
197 pub(crate) surface: wgpu::Surface<'static>,
198 pub(crate) config: wgpu::SurfaceConfiguration,
199 pub(crate) supported_present_modes: Vec<wgpu::PresentMode>,
201
202 pub(crate) pipelines: GpuPipelines,
204 pub(crate) buffers: GpuBuffers,
205 pub(crate) atlas: GlyphAtlas,
206 pub(crate) grid: GridLayout,
207 pub(crate) cursor: CursorState,
208 pub(crate) font: FontState,
209 pub(crate) bg_state: BackgroundImageState,
210 pub(crate) separator: SeparatorConfig,
211
212 pub(crate) scale_factor: f32,
214
215 pub(crate) font_manager: FontManager,
217 pub(crate) scrollbar: Scrollbar,
218
219 pub(crate) cells: Vec<Cell>,
221 pub(crate) dirty_rows: Vec<bool>,
222 pub(crate) row_cache: Vec<Option<RowCacheEntry>>,
223
224 pub(crate) visual_bell_intensity: f32,
226 pub(crate) visual_bell_color: [f32; 3],
227 pub(crate) window_opacity: f32,
228 pub(crate) background_color: [f32; 4],
229 pub(crate) is_focused: bool,
231
232 pub(crate) bg_instances: Vec<BackgroundInstance>,
234 pub(crate) text_instances: Vec<TextInstance>,
235
236 pub(crate) scratch_row_bg: Vec<BackgroundInstance>,
238 pub(crate) scratch_row_text: Vec<TextInstance>,
239 pub(crate) scratch_row_cells: Vec<Cell>,
242
243 pub(crate) scale_context: swash::scale::ScaleContext,
248
249 pub(crate) transparency_affects_only_default_background: bool,
253 pub(crate) keep_text_opaque: bool,
255 pub(crate) link_underline_style: par_term_config::LinkUnderlineStyle,
257
258 pub(crate) gutter_indicators: Vec<(usize, [f32; 4])>,
260}
261
262pub struct CellRendererConfig<'a> {
267 pub font_manager: FontManager,
273 pub font_size: f32,
274 pub cols: usize,
275 pub rows: usize,
276 pub window_padding: f32,
277 pub line_spacing: f32,
278 pub char_spacing: f32,
279 pub scrollbar_position: &'a str,
280 pub scrollbar_width: f32,
281 pub scrollbar_thumb_color: [f32; 4],
282 pub scrollbar_track_color: [f32; 4],
283 pub enable_text_shaping: bool,
284 pub enable_ligatures: bool,
285 pub enable_kerning: bool,
286 pub font_antialias: bool,
287 pub font_hinting: bool,
288 pub font_thin_strokes: par_term_config::ThinStrokesMode,
289 pub minimum_contrast: f32,
290 pub vsync_mode: par_term_config::VsyncMode,
291 pub power_preference: par_term_config::PowerPreference,
292 pub window_opacity: f32,
293 pub background_color: [u8; 3],
294 pub background_image_path: Option<&'a str>,
295 pub background_image_mode: par_term_config::BackgroundImageMode,
296 pub background_image_opacity: f32,
297}
298
299impl CellRenderer {
300 pub async fn new(window: Arc<Window>, config: CellRendererConfig<'_>) -> Result<Self> {
301 let CellRendererConfig {
302 font_manager,
303 font_size,
304 cols,
305 rows,
306 window_padding,
307 line_spacing,
308 char_spacing,
309 scrollbar_position,
310 scrollbar_width,
311 scrollbar_thumb_color,
312 scrollbar_track_color,
313 enable_text_shaping,
314 enable_ligatures,
315 enable_kerning,
316 font_antialias,
317 font_hinting,
318 font_thin_strokes,
319 minimum_contrast,
320 vsync_mode,
321 power_preference,
322 window_opacity,
323 background_color,
324 background_image_path,
325 background_image_mode,
326 background_image_opacity,
327 } = config;
328 #[cfg(target_os = "windows")]
337 let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
338 backends: wgpu::Backends::DX12,
339 ..wgpu::InstanceDescriptor::new_without_display_handle()
340 });
341 #[cfg(target_os = "macos")]
342 let instance = wgpu::Instance::default();
343 #[cfg(target_os = "linux")]
344 let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
345 backends: wgpu::Backends::VULKAN | wgpu::Backends::GL,
346 ..wgpu::InstanceDescriptor::new_without_display_handle()
347 });
348 let surface = instance.create_surface(window.clone())?;
349 let adapter = instance
350 .request_adapter(&wgpu::RequestAdapterOptions {
351 power_preference: power_preference.to_wgpu(),
352 compatible_surface: Some(&surface),
353 force_fallback_adapter: false,
354 apply_limit_buckets: false,
355 })
356 .await
357 .context("Failed to find wgpu adapter")?;
358
359 let (device, queue) = adapter
360 .request_device(&wgpu::DeviceDescriptor {
361 label: Some("device"),
362 required_features: wgpu::Features::empty(),
363 required_limits: surface::texture_limits(adapter.limits().max_texture_dimension_2d),
364 memory_hints: wgpu::MemoryHints::default(),
365 ..Default::default()
366 })
367 .await?;
368 surface::install_nonfatal_error_handler(&device);
369
370 let device = Arc::new(device);
371 let queue = Arc::new(queue);
372
373 let size = window.inner_size();
374 let surface_caps = surface.get_capabilities(&adapter);
375 let surface_format = surface_caps
376 .formats
377 .iter()
378 .copied()
379 .find(|f| !f.is_srgb())
380 .or_else(|| surface_caps.formats.first().copied())
381 .context("Surface reports no supported texture formats")?;
382
383 let supported_present_modes = surface_caps.present_modes.clone();
385
386 let requested_mode = vsync_mode.to_present_mode();
388 let present_mode = if supported_present_modes.contains(&requested_mode) {
389 requested_mode
390 } else {
391 log::warn!(
393 "Requested present mode {:?} not supported (available: {:?}), falling back",
394 requested_mode,
395 supported_present_modes
396 );
397 if supported_present_modes.contains(&wgpu::PresentMode::Fifo) {
398 wgpu::PresentMode::Fifo
399 } else {
400 supported_present_modes
401 .first()
402 .copied()
403 .context("Surface reports no supported present modes")?
404 }
405 };
406
407 let alpha_mode = if surface_caps
410 .alpha_modes
411 .contains(&wgpu::CompositeAlphaMode::PreMultiplied)
412 {
413 wgpu::CompositeAlphaMode::PreMultiplied
414 } else if surface_caps
415 .alpha_modes
416 .contains(&wgpu::CompositeAlphaMode::PostMultiplied)
417 {
418 wgpu::CompositeAlphaMode::PostMultiplied
419 } else if surface_caps
420 .alpha_modes
421 .contains(&wgpu::CompositeAlphaMode::Auto)
422 {
423 wgpu::CompositeAlphaMode::Auto
424 } else {
425 surface_caps
426 .alpha_modes
427 .first()
428 .copied()
429 .context("Surface reports no supported alpha modes")?
430 };
431 log::info!(
432 "Selected alpha mode: {:?} (available: {:?})",
433 alpha_mode,
434 surface_caps.alpha_modes
435 );
436
437 let (surface_width, surface_height) = surface::clamp_surface_extent(
438 size.width,
439 size.height,
440 device.limits().max_texture_dimension_2d,
441 );
442 let config = wgpu::SurfaceConfiguration {
443 usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
444 format: surface_format,
445 color_space: wgpu::SurfaceColorSpace::Auto,
446 width: surface_width,
447 height: surface_height,
448 present_mode,
449 alpha_mode,
450 view_formats: vec![],
451 desired_maximum_frame_latency: SURFACE_FRAME_LATENCY,
452 };
453 log::info!(
454 "Surface configured: {}x{} {:?} present={:?} alpha={:?} color_space={:?} frame_latency={}",
455 surface_width,
456 surface_height,
457 surface_format,
458 present_mode,
459 alpha_mode,
460 config.color_space,
461 SURFACE_FRAME_LATENCY
462 );
463 surface.configure(&device, &config);
464
465 let scale_factor = window.scale_factor() as f32;
466
467 let platform_dpi = if cfg!(target_os = "macos") {
468 MACOS_PLATFORM_DPI
469 } else {
470 DEFAULT_PLATFORM_DPI
471 };
472
473 let base_font_pixels = font_size * platform_dpi / FONT_REFERENCE_DPI;
474 let font_size_pixels = (base_font_pixels * scale_factor).max(1.0);
475
476 let (font_ascent, font_descent, font_leading, char_advance) = {
478 let primary_font = font_manager
479 .get_font(0)
480 .expect("Primary font at index 0 must exist after FontManager initialization");
481 let metrics = primary_font.metrics(&[]);
482 let scale = font_size_pixels / metrics.units_per_em as f32;
483 let glyph_id = primary_font.charmap().map('m');
484 let advance = primary_font.glyph_metrics(&[]).advance_width(glyph_id) * scale;
485 (
486 metrics.ascent * scale,
487 metrics.descent * scale,
488 metrics.leading * scale,
489 advance,
490 )
491 };
492
493 let natural_line_height = font_ascent + font_descent + font_leading;
494 let cell_height = (natural_line_height * line_spacing).max(1.0).round();
499 let cell_width = (char_advance * char_spacing).max(1.0).round();
500
501 let scrollbar = Scrollbar::new(
502 Arc::clone(&device),
503 surface_format,
504 scrollbar_width,
505 scrollbar_position,
506 scrollbar_thumb_color,
507 scrollbar_track_color,
508 );
509
510 let bg_pipeline = pipeline::create_bg_pipeline(&device, surface_format);
512
513 let (atlas_texture, atlas_view, atlas_sampler, atlas_size) =
514 pipeline::create_atlas(&device);
515 let text_bind_group_layout = pipeline::create_text_bind_group_layout(&device);
516 let text_bind_group = pipeline::create_text_bind_group(
517 &device,
518 &text_bind_group_layout,
519 &atlas_view,
520 &atlas_sampler,
521 );
522 let text_pipeline =
523 pipeline::create_text_pipeline(&device, surface_format, &text_bind_group_layout);
524
525 let bg_image_bind_group_layout = pipeline::create_bg_image_bind_group_layout(&device);
526 let bg_image_pipeline = pipeline::create_bg_image_pipeline(
527 &device,
528 surface_format,
529 &bg_image_bind_group_layout,
530 );
531 let bg_image_uniform_buffer = pipeline::create_bg_image_uniform_buffer(&device);
532
533 let (visual_bell_pipeline, visual_bell_bind_group, _, visual_bell_uniform_buffer) =
534 pipeline::create_visual_bell_pipeline(&device, surface_format);
535
536 let opaque_alpha_pipeline = pipeline::create_opaque_alpha_pipeline(&device, surface_format);
537
538 let vertex_buffer = pipeline::create_vertex_buffer(&device);
539
540 let max_bg_instances = render::SingleGridLayout::new(cols, rows).bg_instances();
542 let max_text_instances = cols * rows * TEXT_INSTANCES_PER_CELL;
543 let (bg_instance_buffer, text_instance_buffer) =
544 pipeline::create_instance_buffers(&device, max_bg_instances, max_text_instances);
545
546 let mut renderer = Self {
547 device,
548 queue,
549 surface,
550 config,
551 supported_present_modes,
552 pipelines: GpuPipelines {
553 bg_pipeline,
554 text_pipeline,
555 bg_image_pipeline,
556 visual_bell_pipeline,
557 text_bind_group,
558 text_bind_group_layout,
559 bg_image_bind_group: None,
560 bg_image_bind_group_layout,
561 visual_bell_bind_group,
562 opaque_alpha_pipeline,
563 },
564 buffers: GpuBuffers {
565 vertex_buffer,
566 bg_instance_buffer,
567 text_instance_buffer,
568 bg_image_uniform_buffer,
569 visual_bell_uniform_buffer,
570 max_bg_instances,
571 max_text_instances,
572 actual_bg_instances: 0,
573 actual_text_instances: 0,
574 pane_bg_cursor: 0,
575 pane_text_cursor: 0,
576 overflow_reported: false,
577 },
578 atlas: GlyphAtlas {
579 atlas_texture,
580 atlas_view,
581 glyph_cache: HashMap::new(),
582 lru_head: None,
583 lru_tail: None,
584 atlas_next_x: 0,
585 atlas_next_y: 0,
586 atlas_row_height: 0,
587 atlas_size,
588 solid_pixel_offset: (0, 0),
589 },
590 grid: GridLayout {
591 cols,
592 rows,
593 cell_width,
594 cell_height,
595 window_padding,
596 content_offset_y: 0.0,
597 content_offset_x: 0.0,
598 content_inset_bottom: 0.0,
599 content_inset_right: 0.0,
600 egui_bottom_inset: 0.0,
601 egui_right_inset: 0.0,
602 },
603 cursor: CursorState {
604 pos: (0, 0),
605 opacity: 0.0,
606 style: par_term_emu_core_rust::cursor::CursorStyle::SteadyBlock,
607 color: [1.0, 1.0, 1.0],
608 text_color: None,
609 hidden_for_shader: false,
610 guide_enabled: false,
611 guide_color: [1.0, 1.0, 1.0, DEFAULT_GUIDE_OPACITY],
612 shadow_enabled: false,
613 shadow_color: [0.0, 0.0, 0.0, DEFAULT_SHADOW_ALPHA],
614 shadow_offset: [DEFAULT_SHADOW_OFFSET_PX, DEFAULT_SHADOW_OFFSET_PX],
615 shadow_blur: DEFAULT_SHADOW_BLUR_PX,
616 boost: 0.0,
617 boost_color: [1.0, 1.0, 1.0],
618 unfocused_style: par_term_config::UnfocusedCursorStyle::default(),
619 },
620 font: FontState {
621 base_font_size: font_size,
622 line_spacing,
623 char_spacing,
624 font_ascent,
625 font_descent,
626 font_leading,
627 font_size_pixels,
628 char_advance,
629 enable_text_shaping,
630 enable_ligatures,
631 enable_kerning,
632 font_antialias,
633 font_hinting,
634 font_thin_strokes,
635 minimum_contrast: minimum_contrast.clamp(0.0, 1.0),
636 },
637 bg_state: BackgroundImageState {
638 bg_image_texture: None,
639 bg_image_mode: background_image_mode,
640 bg_image_opacity: background_image_opacity,
641 bg_image_width: 0,
642 bg_image_height: 0,
643 bg_is_solid_color: false,
644 solid_bg_color: [0.0, 0.0, 0.0],
645 pane_bg_cache: HashMap::new(),
646 pane_bg_uniform_cache: HashMap::new(),
647 },
648 separator: SeparatorConfig {
649 enabled: false,
650 thickness: 1.0,
651 opacity: 0.4,
652 exit_color: true,
653 color: [0.5, 0.5, 0.5],
654 visible_marks: Vec::new(),
655 },
656 scale_factor,
657 font_manager,
658 scrollbar,
659 cells: vec![Cell::default(); cols * rows],
660 dirty_rows: vec![true; rows],
661 row_cache: (0..rows).map(|_| None).collect(),
662 is_focused: true,
663 visual_bell_intensity: 0.0,
664 visual_bell_color: [1.0, 1.0, 1.0], window_opacity,
666 background_color: color_u8_to_f32_a(background_color, 1.0),
667 bg_instances: vec![
668 BackgroundInstance {
669 position: [0.0, 0.0],
670 size: [0.0, 0.0],
671 color: [0.0, 0.0, 0.0, 0.0],
672 };
673 max_bg_instances
674 ],
675 text_instances: vec![
676 TextInstance {
677 position: [0.0, 0.0],
678 size: [0.0, 0.0],
679 tex_offset: [0.0, 0.0],
680 tex_size: [0.0, 0.0],
681 color: [0.0, 0.0, 0.0, 0.0],
682 is_colored: 0,
683 };
684 max_text_instances
685 ],
686 transparency_affects_only_default_background: false,
687 keep_text_opaque: true,
688 link_underline_style: par_term_config::LinkUnderlineStyle::default(),
689 gutter_indicators: Vec::new(),
690 scratch_row_bg: Vec::with_capacity(cols),
691 scratch_row_text: Vec::with_capacity(cols * 2),
692 scratch_row_cells: Vec::with_capacity(cols),
693 scale_context: swash::scale::ScaleContext::new(),
694 };
695
696 renderer.upload_solid_pixel();
698
699 log::info!(
700 "CellRenderer::new: background_image_path={:?}",
701 background_image_path
702 );
703 if let Some(path) = background_image_path {
704 if let Err(e) = renderer.load_background_image(path) {
706 log::warn!(
707 "Could not load background image '{}': {} - continuing without background image",
708 path,
709 e
710 );
711 }
712 }
713
714 Ok(renderer)
715 }
716
717 pub(crate) fn upload_solid_pixel(&mut self) {
719 let size = SOLID_PIXEL_SIZE;
720 let white_pixels: Vec<u8> = vec![255; (size * size * 4) as usize];
721
722 self.queue.write_texture(
723 wgpu::TexelCopyTextureInfo {
724 texture: &self.atlas.atlas_texture,
725 mip_level: 0,
726 origin: wgpu::Origin3d {
727 x: self.atlas.atlas_next_x,
728 y: self.atlas.atlas_next_y,
729 z: 0,
730 },
731 aspect: wgpu::TextureAspect::All,
732 },
733 &white_pixels,
734 wgpu::TexelCopyBufferLayout {
735 offset: 0,
736 bytes_per_row: Some(4 * size),
737 rows_per_image: Some(size),
738 },
739 wgpu::Extent3d {
740 width: size,
741 height: size,
742 depth_or_array_layers: 1,
743 },
744 );
745
746 self.atlas.solid_pixel_offset = (self.atlas.atlas_next_x, self.atlas.atlas_next_y);
747 self.atlas.atlas_next_x += size + ATLAS_GLYPH_PADDING;
748 self.atlas.atlas_row_height = self.atlas.atlas_row_height.max(size);
749 }
750
751 pub fn device(&self) -> &wgpu::Device {
752 &self.device
753 }
754 pub fn queue(&self) -> &wgpu::Queue {
755 &self.queue
756 }
757 pub fn surface_format(&self) -> wgpu::TextureFormat {
758 self.config.format
759 }
760 pub fn keep_text_opaque(&self) -> bool {
761 self.keep_text_opaque
762 }
763
764 pub fn update_cells(&mut self, new_cells: &[Cell]) -> bool {
772 let n = new_cells.len().min(self.cells.len());
773 let mut changed = false;
774 for row in 0..self.grid.rows {
775 let start = row * self.grid.cols;
776 if start >= n {
777 break;
778 }
779 let end = ((row + 1) * self.grid.cols).min(n);
780 let row_slice = &new_cells[start..end];
781 if row_slice != &self.cells[start..end] {
782 self.cells[start..end].clone_from_slice(row_slice);
783 self.dirty_rows[row] = true;
784 changed = true;
785 }
786 }
787 changed
788 }
789
790 pub fn clear_all_cells(&mut self) {
792 for cell in &mut self.cells {
793 *cell = Cell::default();
794 }
795 self.dirty_rows.fill(true);
796 }
797
798 pub fn update_graphics(
799 &mut self,
800 _graphics: &[par_term_emu_core_rust::graphics::TerminalGraphic],
801 _scroll_offset: usize,
802 _scrollback_len: usize,
803 _visible_lines: usize,
804 ) -> Result<()> {
805 Ok(())
806 }
807}