use zellij_utils::async_std::task; use zellij_utils::errors::{prelude::*, BackgroundJobContext, ContextType}; use std::collections::HashMap; use std::sync::{ atomic::{AtomicBool, Ordering}, Arc, }; use std::time::{Duration, Instant}; use crate::panes::PaneId; use crate::screen::ScreenInstruction; use crate::thread_bus::Bus; #[derive(Debug, Clone, Eq, PartialEq, Hash)] pub enum BackgroundJob { DisplayPaneError(Vec, String), AnimatePluginLoading(u32), // u32 - plugin_id StopPluginLoadingAnimation(u32), // u32 - plugin_id Exit, } impl From<&BackgroundJob> for BackgroundJobContext { fn from(background_job: &BackgroundJob) -> Self { match *background_job { BackgroundJob::DisplayPaneError(..) => BackgroundJobContext::DisplayPaneError, BackgroundJob::AnimatePluginLoading(..) => BackgroundJobContext::AnimatePluginLoading, BackgroundJob::StopPluginLoadingAnimation(..) => { BackgroundJobContext::StopPluginLoadingAnimation }, BackgroundJob::Exit => BackgroundJobContext::Exit, } } } static FLASH_DURATION_MS: u64 = 1000; static PLUGIN_ANIMATION_OFFSET_DURATION_MD: u64 = 500; pub(crate) fn background_jobs_main(bus: Bus) -> Result<()> { let err_context = || "failed to write to pty".to_string(); let mut running_jobs: HashMap = HashMap::new(); let mut loading_plugins: HashMap> = HashMap::new(); // u32 - plugin_id loop { let (event, mut err_ctx) = bus.recv().with_context(err_context)?; err_ctx.add_call(ContextType::BackgroundJob((&event).into())); let job = event.clone(); match event { BackgroundJob::DisplayPaneError(pane_ids, text) => { if job_already_running(job, &mut running_jobs) { continue; } task::spawn({ let senders = bus.senders.clone(); async move { let _ = senders.send_to_screen( ScreenInstruction::AddRedPaneFrameColorOverride( pane_ids.clone(), Some(text), ), ); task::sleep(std::time::Duration::from_millis(FLASH_DURATION_MS)).await; let _ = senders.send_to_screen( ScreenInstruction::ClearPaneFrameColorOverride(pane_ids), ); } }); }, BackgroundJob::AnimatePluginLoading(pid) => { let loading_plugin = Arc::new(AtomicBool::new(true)); if job_already_running(job, &mut running_jobs) { continue; } task::spawn({ let senders = bus.senders.clone(); let loading_plugin = loading_plugin.clone(); async move { while loading_plugin.load(Ordering::SeqCst) { let _ = senders.send_to_screen( ScreenInstruction::ProgressPluginLoadingOffset(pid), ); task::sleep(std::time::Duration::from_millis( PLUGIN_ANIMATION_OFFSET_DURATION_MD, )) .await; } } }); loading_plugins.insert(pid, loading_plugin); }, BackgroundJob::StopPluginLoadingAnimation(pid) => { if let Some(loading_plugin) = loading_plugins.remove(&pid) { loading_plugin.store(false, Ordering::SeqCst); } }, BackgroundJob::Exit => { for loading_plugin in loading_plugins.values() { loading_plugin.store(false, Ordering::SeqCst); } return Ok(()); }, } } } fn job_already_running( job: BackgroundJob, running_jobs: &mut HashMap, ) -> bool { match running_jobs.get_mut(&job) { Some(current_running_job_start_time) => { if current_running_job_start_time.elapsed() > Duration::from_millis(FLASH_DURATION_MS) { *current_running_job_start_time = Instant::now(); false } else { true } }, None => { running_jobs.insert(job.clone(), Instant::now()); false }, } }