Skip to main content

par_term/tab/
refresh_task.rs

1//! Tab refresh polling task management.
2//!
3//! Provides methods for starting and stopping the background async task
4//! that polls the terminal for new output and triggers window redraws.
5//! Uses adaptive polling with exponential backoff for inactive tabs.
6
7use crate::tab::Tab;
8use std::sync::Arc;
9use std::sync::atomic::Ordering;
10use tokio::runtime::Runtime;
11
12impl Tab {
13    /// Start the refresh polling task for this tab
14    pub fn start_refresh_task(
15        &mut self,
16        runtime: Arc<Runtime>,
17        window: Arc<winit::window::Window>,
18        active_fps: u32,
19        inactive_fps: u32,
20    ) {
21        let terminal_clone = Arc::clone(&self.terminal);
22        let is_active = Arc::clone(&self.is_active);
23        let active_interval_ms = (1000 / active_fps.max(1)) as u64;
24        let inactive_interval_ms = (1000 / inactive_fps.max(1)) as u64;
25
26        let handle = runtime.spawn(async move {
27            let mut last_gen = 0u64;
28            let mut idle_streak = 0u32;
29            const MAX_INACTIVE_IDLE_INTERVAL_MS: u64 = 250;
30
31            loop {
32                let is_active_now = is_active.load(Ordering::Relaxed);
33                // Keep the active tab responsive: only apply backoff to inactive tabs.
34                let interval_ms = if is_active_now {
35                    active_interval_ms
36                } else if idle_streak > 0 {
37                    (inactive_interval_ms << idle_streak.min(4)).min(MAX_INACTIVE_IDLE_INTERVAL_MS)
38                } else {
39                    inactive_interval_ms
40                };
41                tokio::time::sleep(tokio::time::Duration::from_millis(interval_ms)).await;
42
43                let should_redraw = if let Ok(term) = terminal_clone.try_read() {
44                    let current_gen = term.update_generation();
45                    if current_gen > last_gen {
46                        last_gen = current_gen;
47                        true
48                    } else {
49                        false
50                    }
51                } else {
52                    false
53                };
54
55                if should_redraw {
56                    idle_streak = 0;
57                    window.request_redraw();
58                } else if is_active_now {
59                    idle_streak = 0;
60                } else {
61                    idle_streak = idle_streak.saturating_add(1);
62                }
63            }
64        });
65
66        self.refresh_task = Some(handle);
67    }
68
69    /// Stop the refresh polling task
70    pub fn stop_refresh_task(&mut self) {
71        if let Some(handle) = self.refresh_task.take() {
72            handle.abort();
73        }
74    }
75}