zellij_server/plugins/
wasm_bridge.rs

1use super::{PluginId, PluginInstruction};
2use crate::plugins::pipes::{
3    apply_pipe_message_to_plugin, pipes_to_block_or_unblock, PendingPipes, PipeStateChange,
4};
5use crate::plugins::plugin_loader::PluginLoader;
6use crate::plugins::plugin_map::{AtomicEvent, PluginEnv, PluginMap, RunningPlugin, Subscriptions};
7
8use crate::plugins::plugin_worker::MessageToWorker;
9use crate::plugins::watch_filesystem::watch_filesystem;
10use crate::plugins::zellij_exports::{wasi_read_string, wasi_write_object};
11use async_channel::Sender;
12use async_std::task::{self, JoinHandle};
13use highway::{HighwayHash, PortableHash};
14use log::info;
15use notify_debouncer_full::{notify::RecommendedWatcher, Debouncer, FileIdMap};
16use std::{
17    collections::{BTreeMap, HashMap, HashSet},
18    path::PathBuf,
19    str::FromStr,
20    sync::{Arc, Mutex},
21};
22use wasmtime::{Engine, Module};
23use zellij_utils::consts::{ZELLIJ_CACHE_DIR, ZELLIJ_TMP_DIR};
24use zellij_utils::data::{
25    FloatingPaneCoordinates, InputMode, PermissionStatus, PermissionType, PipeMessage, PipeSource,
26};
27use zellij_utils::downloader::Downloader;
28use zellij_utils::input::keybinds::Keybinds;
29use zellij_utils::input::permission::PermissionCache;
30use zellij_utils::plugin_api::event::ProtobufEvent;
31
32use prost::Message;
33
34use crate::panes::PaneId;
35use crate::{
36    background_jobs::BackgroundJob, screen::ScreenInstruction, thread_bus::ThreadSenders,
37    ui::loading_indication::LoadingIndication, ClientId, ServerInstruction,
38};
39use zellij_utils::{
40    data::{Event, EventType, PluginCapabilities},
41    errors::prelude::*,
42    input::{
43        command::TerminalAction,
44        layout::{Layout, PluginUserConfiguration, RunPlugin, RunPluginLocation, RunPluginOrAlias},
45        plugins::PluginConfig,
46    },
47    ipc::ClientAttributes,
48    pane_size::Size,
49};
50
51#[derive(Debug, Clone)]
52pub enum EventOrPipeMessage {
53    Event(Event),
54    PipeMessage(PipeMessage),
55}
56
57#[derive(Debug, Clone, Default)]
58pub struct PluginRenderAsset {
59    // TODO: naming
60    pub client_id: ClientId,
61    pub plugin_id: PluginId,
62    pub bytes: Vec<u8>,
63    pub cli_pipes: HashMap<String, PipeStateChange>,
64}
65
66impl PluginRenderAsset {
67    pub fn new(plugin_id: PluginId, client_id: ClientId, bytes: Vec<u8>) -> Self {
68        PluginRenderAsset {
69            client_id,
70            plugin_id,
71            bytes,
72            ..Default::default()
73        }
74    }
75    pub fn with_pipes(mut self, cli_pipes: HashMap<String, PipeStateChange>) -> Self {
76        self.cli_pipes = cli_pipes;
77        self
78    }
79}
80
81pub struct WasmBridge {
82    connected_clients: Arc<Mutex<Vec<ClientId>>>,
83    senders: ThreadSenders,
84    engine: Engine,
85    plugin_dir: PathBuf,
86    plugin_cache: Arc<Mutex<HashMap<PathBuf, Module>>>,
87    plugin_map: Arc<Mutex<PluginMap>>,
88    next_plugin_id: PluginId,
89    plugin_ids_waiting_for_permission_request: HashSet<PluginId>,
90    cached_events_for_pending_plugins: HashMap<PluginId, Vec<EventOrPipeMessage>>,
91    cached_resizes_for_pending_plugins: HashMap<PluginId, (usize, usize)>, // (rows, columns)
92    cached_worker_messages: HashMap<PluginId, Vec<(ClientId, String, String, String)>>, // Vec<clientid,
93    // worker_name,
94    // message,
95    // payload>
96    loading_plugins: HashMap<(PluginId, RunPlugin), JoinHandle<()>>, // plugin_id to join-handle
97    pending_plugin_reloads: HashSet<RunPlugin>,
98    path_to_default_shell: PathBuf,
99    watcher: Option<Debouncer<RecommendedWatcher, FileIdMap>>,
100    zellij_cwd: PathBuf,
101    capabilities: PluginCapabilities,
102    client_attributes: ClientAttributes,
103    default_shell: Option<TerminalAction>,
104    default_layout: Box<Layout>,
105    cached_plugin_map:
106        HashMap<RunPluginLocation, HashMap<PluginUserConfiguration, Vec<(PluginId, ClientId)>>>,
107    pending_pipes: PendingPipes,
108    layout_dir: Option<PathBuf>,
109    default_mode: InputMode,
110    default_keybinds: Keybinds,
111    keybinds: HashMap<ClientId, Keybinds>,
112    base_modes: HashMap<ClientId, InputMode>,
113    downloader: Downloader,
114}
115
116impl WasmBridge {
117    pub fn new(
118        senders: ThreadSenders,
119        engine: Engine,
120        plugin_dir: PathBuf,
121        path_to_default_shell: PathBuf,
122        zellij_cwd: PathBuf,
123        capabilities: PluginCapabilities,
124        client_attributes: ClientAttributes,
125        default_shell: Option<TerminalAction>,
126        default_layout: Box<Layout>,
127        layout_dir: Option<PathBuf>,
128        default_mode: InputMode,
129        default_keybinds: Keybinds,
130    ) -> Self {
131        let plugin_map = Arc::new(Mutex::new(PluginMap::default()));
132        let connected_clients: Arc<Mutex<Vec<ClientId>>> = Arc::new(Mutex::new(vec![]));
133        let plugin_cache: Arc<Mutex<HashMap<PathBuf, Module>>> =
134            Arc::new(Mutex::new(HashMap::new()));
135        let watcher = None;
136        let downloader = Downloader::new(ZELLIJ_CACHE_DIR.to_path_buf());
137        WasmBridge {
138            connected_clients,
139            senders,
140            engine,
141            plugin_dir,
142            plugin_cache,
143            plugin_map,
144            path_to_default_shell,
145            watcher,
146            next_plugin_id: 0,
147            cached_events_for_pending_plugins: HashMap::new(),
148            plugin_ids_waiting_for_permission_request: HashSet::new(),
149            cached_resizes_for_pending_plugins: HashMap::new(),
150            cached_worker_messages: HashMap::new(),
151            loading_plugins: HashMap::new(),
152            pending_plugin_reloads: HashSet::new(),
153            zellij_cwd,
154            capabilities,
155            client_attributes,
156            default_shell,
157            default_layout,
158            cached_plugin_map: HashMap::new(),
159            pending_pipes: Default::default(),
160            layout_dir,
161            default_mode,
162            default_keybinds,
163            keybinds: HashMap::new(),
164            base_modes: HashMap::new(),
165            downloader,
166        }
167    }
168    pub fn load_plugin(
169        &mut self,
170        run: &Option<RunPlugin>,
171        tab_index: Option<usize>,
172        size: Size,
173        cwd: Option<PathBuf>,
174        skip_cache: bool,
175        client_id: Option<ClientId>,
176        cli_client_id: Option<ClientId>,
177    ) -> Result<(PluginId, ClientId)> {
178        // returns the plugin id
179        let err_context = move || format!("failed to load plugin");
180
181        let client_id = client_id
182            .or_else(|| {
183                self.connected_clients
184                    .lock()
185                    .unwrap()
186                    .iter()
187                    .next()
188                    .copied()
189            })
190            .with_context(|| {
191                "Plugins must have a client id, none was provided and none are connected"
192            })?;
193
194        let plugin_id = self.next_plugin_id;
195
196        match run {
197            Some(run) => {
198                let mut plugin = PluginConfig::from_run_plugin(run)
199                    .with_context(|| format!("failed to resolve plugin {run:?}"))
200                    .with_context(err_context)?;
201                let plugin_name = run.location.to_string();
202
203                self.cached_events_for_pending_plugins
204                    .insert(plugin_id, vec![]);
205                self.cached_resizes_for_pending_plugins
206                    .insert(plugin_id, (size.rows, size.cols));
207
208                let load_plugin_task = task::spawn({
209                    let plugin_dir = self.plugin_dir.clone();
210                    let plugin_cache = self.plugin_cache.clone();
211                    let senders = self.senders.clone();
212                    let engine = self.engine.clone();
213                    let plugin_map = self.plugin_map.clone();
214                    let connected_clients = self.connected_clients.clone();
215                    let path_to_default_shell = self.path_to_default_shell.clone();
216                    let zellij_cwd = cwd.unwrap_or_else(|| self.zellij_cwd.clone());
217                    let capabilities = self.capabilities.clone();
218                    let client_attributes = self.client_attributes.clone();
219                    let default_shell = self.default_shell.clone();
220                    let default_layout = self.default_layout.clone();
221                    let layout_dir = self.layout_dir.clone();
222                    let downloader = self.downloader.clone();
223                    let default_mode = self
224                        .base_modes
225                        .get(&client_id)
226                        .copied()
227                        .unwrap_or(self.default_mode);
228                    let keybinds = self
229                        .keybinds
230                        .get(&client_id)
231                        .cloned()
232                        .unwrap_or_else(|| self.default_keybinds.clone());
233                    async move {
234                        let _ = senders.send_to_background_jobs(
235                            BackgroundJob::AnimatePluginLoading(plugin_id),
236                        );
237                        let mut loading_indication = LoadingIndication::new(plugin_name.clone());
238
239                        if let RunPluginLocation::Remote(url) = &plugin.location {
240                            let file_name: String = PortableHash::default()
241                                .hash128(url.as_bytes())
242                                .iter()
243                                .map(ToString::to_string)
244                                .collect();
245
246                            // if the url is already in cache, we'll use that version, otherwise
247                            // we'll download it, place it in cache and then use it
248                            match downloader.download(url, Some(&file_name)).await {
249                                Ok(_) => plugin.path = ZELLIJ_CACHE_DIR.join(&file_name),
250                                Err(e) => handle_plugin_loading_failure(
251                                    &senders,
252                                    plugin_id,
253                                    &mut loading_indication,
254                                    e,
255                                    cli_client_id,
256                                ),
257                            }
258                        }
259
260                        match PluginLoader::start_plugin(
261                            plugin_id,
262                            client_id,
263                            &plugin,
264                            tab_index,
265                            plugin_dir,
266                            plugin_cache,
267                            senders.clone(),
268                            engine,
269                            plugin_map.clone(),
270                            size,
271                            connected_clients.clone(),
272                            &mut loading_indication,
273                            path_to_default_shell,
274                            zellij_cwd.clone(),
275                            capabilities,
276                            client_attributes,
277                            default_shell,
278                            default_layout,
279                            skip_cache,
280                            layout_dir,
281                            default_mode,
282                            keybinds,
283                        ) {
284                            Ok(_) => {
285                                let plugin_list = plugin_map.lock().unwrap().list_plugins();
286                                handle_plugin_successful_loading(&senders, plugin_id, plugin_list);
287                            },
288                            Err(e) => handle_plugin_loading_failure(
289                                &senders,
290                                plugin_id,
291                                &mut loading_indication,
292                                e,
293                                cli_client_id,
294                            ),
295                        }
296                        let _ = senders.send_to_plugin(PluginInstruction::ApplyCachedEvents {
297                            plugin_ids: vec![plugin_id],
298                            done_receiving_permissions: false,
299                        });
300                    }
301                });
302                self.loading_plugins
303                    .insert((plugin_id, run.clone()), load_plugin_task);
304                self.next_plugin_id += 1;
305            },
306            None => {
307                self.next_plugin_id += 1;
308                let mut loading_indication = LoadingIndication::new(format!("{}", plugin_id));
309                handle_plugin_loading_failure(
310                    &self.senders,
311                    plugin_id,
312                    &mut loading_indication,
313                    "Failed to resolve plugin alias",
314                    None,
315                );
316            },
317        }
318        Ok((plugin_id, client_id))
319    }
320    pub fn unload_plugin(&mut self, pid: PluginId) -> Result<()> {
321        info!("Bye from plugin {}", &pid);
322        let mut plugin_map = self.plugin_map.lock().unwrap();
323        for ((plugin_id, client_id), (running_plugin, subscriptions, workers)) in
324            plugin_map.remove_plugins(pid)
325        {
326            for (_worker_name, worker_sender) in workers {
327                drop(worker_sender.send(MessageToWorker::Exit));
328            }
329            {
330                // if the plugin was intercepting key presses and for some reason did not clear
331                // this state, we make sure to do it ourselves so that the user will not get stuck
332                if running_plugin.lock().unwrap().intercepting_key_presses() {
333                    let _ = self
334                        .senders
335                        .send_to_screen(ScreenInstruction::ClearKeyPressesIntercepts(client_id));
336                }
337            }
338            let subscriptions = subscriptions.lock().unwrap();
339            if subscriptions.contains(&EventType::BeforeClose) {
340                let mut running_plugin = running_plugin.lock().unwrap();
341                match apply_before_close_event_to_plugin(
342                    pid,
343                    client_id,
344                    &mut running_plugin,
345                    self.senders.clone(),
346                ) {
347                    Ok(()) => {},
348                    Err(e) => {
349                        log::error!("{:?}", e);
350
351                        // https://stackoverflow.com/questions/66450942/in-rust-is-there-a-way-to-make-literal-newlines-in-r-using-windows-c
352                        let stringified_error = format!("{:?}", e).replace("\n", "\n\r");
353
354                        handle_plugin_crash(plugin_id, stringified_error, self.senders.clone());
355                    },
356                }
357                let cache_dir = running_plugin.store.data().plugin_own_data_dir.clone();
358                if let Err(e) = std::fs::remove_dir_all(cache_dir) {
359                    log::error!("Failed to remove cache dir for plugin: {:?}", e);
360                }
361            } else {
362                // this is duplicated because of locking/unlocking order between running_plugin and
363                // subscriptions
364                let running_plugin = running_plugin.lock().unwrap();
365                let cache_dir = running_plugin.store.data().plugin_own_data_dir.clone();
366                if let Err(e) = std::fs::remove_dir_all(cache_dir) {
367                    log::error!("Failed to remove cache dir for plugin: {:?}", e);
368                }
369            }
370        }
371        self.cached_plugin_map.clear();
372        let mut pipes_to_unblock = self.pending_pipes.unload_plugin(&pid);
373        for pipe_name in pipes_to_unblock.drain(..) {
374            let _ = self
375                .senders
376                .send_to_server(ServerInstruction::UnblockCliPipeInput(pipe_name))
377                .context("failed to unblock input pipe");
378        }
379        let plugin_list = plugin_map.list_plugins();
380        let _ = self
381            .senders
382            .send_to_background_jobs(BackgroundJob::ReportPluginList(plugin_list));
383        Ok(())
384    }
385    pub fn reload_plugin_with_id(&mut self, plugin_id: u32) -> Result<()> {
386        let Some(run_plugin) = self.run_plugin_of_plugin_id(plugin_id).map(|r| r.clone()) else {
387            log::error!("Failed to find plugin with id: {}", plugin_id);
388            return Ok(());
389        };
390
391        let (rows, columns) = self.size_of_plugin_id(plugin_id).unwrap_or((0, 0));
392        self.cached_events_for_pending_plugins
393            .insert(plugin_id, vec![]);
394        self.cached_resizes_for_pending_plugins
395            .insert(plugin_id, (rows, columns));
396
397        let mut loading_indication = LoadingIndication::new(run_plugin.location.to_string());
398        self.start_plugin_loading_indication(&[plugin_id], &loading_indication);
399        let load_plugin_task = task::spawn({
400            let plugin_dir = self.plugin_dir.clone();
401            let plugin_cache = self.plugin_cache.clone();
402            let senders = self.senders.clone();
403            let engine = self.engine.clone();
404            let plugin_map = self.plugin_map.clone();
405            let connected_clients = self.connected_clients.clone();
406            let path_to_default_shell = self.path_to_default_shell.clone();
407            let capabilities = self.capabilities.clone();
408            let client_attributes = self.client_attributes.clone();
409            let default_shell = self.default_shell.clone();
410            let default_layout = self.default_layout.clone();
411            let layout_dir = self.layout_dir.clone();
412            async move {
413                match PluginLoader::reload_plugin(
414                    plugin_id,
415                    plugin_dir.clone(),
416                    plugin_cache.clone(),
417                    senders.clone(),
418                    engine.clone(),
419                    plugin_map.clone(),
420                    connected_clients.clone(),
421                    &mut loading_indication,
422                    path_to_default_shell.clone(),
423                    capabilities.clone(),
424                    client_attributes.clone(),
425                    default_shell.clone(),
426                    default_layout.clone(),
427                    layout_dir.clone(),
428                ) {
429                    Ok(_) => {
430                        let plugin_list = plugin_map.lock().unwrap().list_plugins();
431                        handle_plugin_successful_loading(&senders, plugin_id, plugin_list);
432                    },
433                    Err(e) => {
434                        handle_plugin_loading_failure(
435                            &senders,
436                            plugin_id,
437                            &mut loading_indication,
438                            &e,
439                            None,
440                        );
441                    },
442                }
443                let _ = senders.send_to_plugin(PluginInstruction::ApplyCachedEvents {
444                    plugin_ids: vec![plugin_id],
445                    done_receiving_permissions: false,
446                });
447            }
448        });
449        self.loading_plugins
450            .insert((plugin_id, run_plugin.clone()), load_plugin_task);
451        Ok(())
452    }
453    pub fn reload_plugin(&mut self, run_plugin: &RunPlugin) -> Result<()> {
454        if self.plugin_is_currently_being_loaded(&run_plugin.location) {
455            self.pending_plugin_reloads.insert(run_plugin.clone());
456            return Ok(());
457        }
458
459        let plugin_ids = self
460            .all_plugin_ids_for_plugin_location(&run_plugin.location, &run_plugin.configuration)?;
461        for plugin_id in &plugin_ids {
462            let (rows, columns) = self.size_of_plugin_id(*plugin_id).unwrap_or((0, 0));
463            self.cached_events_for_pending_plugins
464                .insert(*plugin_id, vec![]);
465            self.cached_resizes_for_pending_plugins
466                .insert(*plugin_id, (rows, columns));
467        }
468
469        let first_plugin_id = *plugin_ids.get(0).unwrap(); // this is safe becaise the above
470                                                           // methods always returns at least 1 id
471        let mut loading_indication = LoadingIndication::new(run_plugin.location.to_string());
472        self.start_plugin_loading_indication(&plugin_ids, &loading_indication);
473        let load_plugin_task = task::spawn({
474            let plugin_dir = self.plugin_dir.clone();
475            let plugin_cache = self.plugin_cache.clone();
476            let senders = self.senders.clone();
477            let engine = self.engine.clone();
478            let plugin_map = self.plugin_map.clone();
479            let connected_clients = self.connected_clients.clone();
480            let path_to_default_shell = self.path_to_default_shell.clone();
481            let zellij_cwd = self.zellij_cwd.clone();
482            let capabilities = self.capabilities.clone();
483            let client_attributes = self.client_attributes.clone();
484            let default_shell = self.default_shell.clone();
485            let default_layout = self.default_layout.clone();
486            let layout_dir = self.layout_dir.clone();
487            async move {
488                match PluginLoader::reload_plugin(
489                    first_plugin_id,
490                    plugin_dir.clone(),
491                    plugin_cache.clone(),
492                    senders.clone(),
493                    engine.clone(),
494                    plugin_map.clone(),
495                    connected_clients.clone(),
496                    &mut loading_indication,
497                    path_to_default_shell.clone(),
498                    capabilities.clone(),
499                    client_attributes.clone(),
500                    default_shell.clone(),
501                    default_layout.clone(),
502                    layout_dir.clone(),
503                ) {
504                    Ok(_) => {
505                        let plugin_list = plugin_map.lock().unwrap().list_plugins();
506                        handle_plugin_successful_loading(&senders, first_plugin_id, plugin_list);
507                        for plugin_id in &plugin_ids {
508                            if plugin_id == &first_plugin_id {
509                                // no need to reload the plugin we just reloaded
510                                continue;
511                            }
512                            let mut loading_indication = LoadingIndication::new("".into());
513                            match PluginLoader::reload_plugin_from_memory(
514                                *plugin_id,
515                                plugin_dir.clone(),
516                                plugin_cache.clone(),
517                                senders.clone(),
518                                engine.clone(),
519                                plugin_map.clone(),
520                                connected_clients.clone(),
521                                &mut loading_indication,
522                                path_to_default_shell.clone(),
523                                zellij_cwd.clone(),
524                                capabilities.clone(),
525                                client_attributes.clone(),
526                                default_shell.clone(),
527                                default_layout.clone(),
528                                layout_dir.clone(),
529                            ) {
530                                Ok(_) => {
531                                    let plugin_list = plugin_map.lock().unwrap().list_plugins();
532                                    handle_plugin_successful_loading(
533                                        &senders,
534                                        *plugin_id,
535                                        plugin_list,
536                                    );
537                                },
538                                Err(e) => handle_plugin_loading_failure(
539                                    &senders,
540                                    *plugin_id,
541                                    &mut loading_indication,
542                                    e,
543                                    None,
544                                ),
545                            }
546                        }
547                    },
548                    Err(e) => {
549                        for plugin_id in &plugin_ids {
550                            handle_plugin_loading_failure(
551                                &senders,
552                                *plugin_id,
553                                &mut loading_indication,
554                                &e,
555                                None,
556                            );
557                        }
558                    },
559                }
560                let _ = senders.send_to_plugin(PluginInstruction::ApplyCachedEvents {
561                    plugin_ids,
562                    done_receiving_permissions: false,
563                });
564            }
565        });
566        self.loading_plugins
567            .insert((first_plugin_id, run_plugin.clone()), load_plugin_task);
568        Ok(())
569    }
570    pub fn add_client(&mut self, client_id: ClientId) -> Result<()> {
571        let mut loading_indication = LoadingIndication::new("".into());
572        match PluginLoader::add_client(
573            client_id,
574            self.plugin_dir.clone(),
575            self.plugin_cache.clone(),
576            self.senders.clone(),
577            self.engine.clone(),
578            self.plugin_map.clone(),
579            self.connected_clients.clone(),
580            &mut loading_indication,
581            self.path_to_default_shell.clone(),
582            self.zellij_cwd.clone(),
583            self.capabilities.clone(),
584            self.client_attributes.clone(),
585            self.default_shell.clone(),
586            self.default_layout.clone(),
587            self.layout_dir.clone(),
588            self.default_mode,
589            self.keybinds
590                .get(&client_id)
591                .cloned()
592                .unwrap_or_else(|| self.default_keybinds.clone()),
593        ) {
594            Ok(_) => {
595                let _ = self
596                    .senders
597                    .send_to_screen(ScreenInstruction::RequestStateUpdateForPlugins);
598                Ok(())
599            },
600            Err(e) => Err(e),
601        }
602    }
603    pub fn resize_plugin(
604        &mut self,
605        pid: PluginId,
606        new_columns: usize,
607        new_rows: usize,
608        shutdown_sender: Sender<()>,
609    ) -> Result<()> {
610        let err_context = move || format!("failed to resize plugin {pid}");
611
612        let plugins_to_resize: Vec<(PluginId, ClientId, Arc<Mutex<RunningPlugin>>)> = self
613            .plugin_map
614            .lock()
615            .unwrap()
616            .running_plugins()
617            .iter()
618            .cloned()
619            .filter(|(plugin_id, _client_id, _running_plugin)| {
620                !self
621                    .cached_resizes_for_pending_plugins
622                    .contains_key(&plugin_id)
623            })
624            .collect();
625        for (plugin_id, client_id, running_plugin) in plugins_to_resize {
626            if plugin_id == pid {
627                let event_id = running_plugin
628                    .lock()
629                    .unwrap()
630                    .next_event_id(AtomicEvent::Resize);
631                task::spawn({
632                    let senders = self.senders.clone();
633                    let running_plugin = running_plugin.clone();
634                    let _s = shutdown_sender.clone();
635                    async move {
636                        let mut running_plugin = running_plugin.lock().unwrap();
637                        let _s = _s; // guard to allow the task to complete before cleanup/shutdown
638                        if running_plugin.apply_event_id(AtomicEvent::Resize, event_id) {
639                            let old_rows = running_plugin.rows;
640                            let old_columns = running_plugin.columns;
641                            running_plugin.rows = new_rows;
642                            running_plugin.columns = new_columns;
643
644                            // in the below conditional, we check if event_id == 0 so that we'll
645                            // make sure to always render on the first resize event
646                            if old_rows != new_rows || old_columns != new_columns || event_id == 0 {
647                                let rendered_bytes = running_plugin
648                                    .instance
649                                    .clone()
650                                    .get_typed_func::<(i32, i32), ()>(
651                                        &mut running_plugin.store,
652                                        "render",
653                                    )
654                                    .and_then(|render| {
655                                        render.call(
656                                            &mut running_plugin.store,
657                                            (new_rows as i32, new_columns as i32),
658                                        )
659                                    })
660                                    .and_then(|_| wasi_read_string(running_plugin.store.data()))
661                                    .with_context(err_context);
662                                match rendered_bytes {
663                                    Ok(rendered_bytes) => {
664                                        let plugin_render_asset = PluginRenderAsset::new(
665                                            plugin_id,
666                                            client_id,
667                                            rendered_bytes.as_bytes().to_vec(),
668                                        );
669                                        senders
670                                            .send_to_screen(ScreenInstruction::PluginBytes(vec![
671                                                plugin_render_asset,
672                                            ]))
673                                            .unwrap();
674                                    },
675                                    Err(e) => log::error!("{}", e),
676                                }
677                            }
678                        }
679                    }
680                });
681            }
682        }
683        for (plugin_id, current_size) in self.cached_resizes_for_pending_plugins.iter_mut() {
684            if *plugin_id == pid {
685                current_size.0 = new_rows;
686                current_size.1 = new_columns;
687            }
688        }
689        Ok(())
690    }
691    pub fn update_plugins(
692        &mut self,
693        mut updates: Vec<(Option<PluginId>, Option<ClientId>, Event)>,
694        shutdown_sender: Sender<()>,
695    ) -> Result<()> {
696        let plugins_to_update: Vec<(
697            PluginId,
698            ClientId,
699            Arc<Mutex<RunningPlugin>>,
700            Arc<Mutex<Subscriptions>>,
701        )> = self
702            .plugin_map
703            .lock()
704            .unwrap()
705            .running_plugins_and_subscriptions()
706            .iter()
707            .cloned()
708            .filter(|(plugin_id, _client_id, _running_plugin, _subscriptions)| {
709                !&self
710                    .cached_events_for_pending_plugins
711                    .contains_key(&plugin_id)
712            })
713            .collect();
714        task::spawn({
715            let mut updates = updates.clone();
716            let senders = self.senders.clone();
717            let s = shutdown_sender.clone();
718            async move {
719                let _s = s;
720                for (pid, cid, event) in updates.drain(..) {
721                    for (plugin_id, client_id, running_plugin, subscriptions) in &plugins_to_update
722                    {
723                        let subs = subscriptions.lock().unwrap().clone();
724                        // FIXME: This is very janky... Maybe I should write my own macro for Event -> EventType?
725                        if let Ok(event_type) = EventType::from_str(&event.to_string()) {
726                            if (subs.contains(&event_type)
727                                || event_type == EventType::PermissionRequestResult)
728                                && Self::message_is_directed_at_plugin(
729                                    pid, cid, plugin_id, client_id,
730                                )
731                            {
732                                let mut running_plugin = running_plugin.lock().unwrap();
733                                let mut plugin_render_assets = vec![];
734                                match apply_event_to_plugin(
735                                    *plugin_id,
736                                    *client_id,
737                                    &mut running_plugin,
738                                    &event,
739                                    &mut plugin_render_assets,
740                                    senders.clone(),
741                                ) {
742                                    Ok(()) => {
743                                        let _ = senders.send_to_screen(
744                                            ScreenInstruction::PluginBytes(plugin_render_assets),
745                                        );
746                                    },
747                                    Err(e) => {
748                                        log::error!("{:?}", e);
749
750                                        // https://stackoverflow.com/questions/66450942/in-rust-is-there-a-way-to-make-literal-newlines-in-r-using-windows-c
751                                        let stringified_error =
752                                            format!("{:?}", e).replace("\n", "\n\r");
753
754                                        handle_plugin_crash(
755                                            *plugin_id,
756                                            stringified_error,
757                                            senders.clone(),
758                                        );
759                                    },
760                                }
761                            }
762                        }
763                    }
764                }
765            }
766        });
767        // loop once more to update the cached events for the pending plugins (probably currently
768        // being loaded, we'll send them these events when they load)
769        for (pid, _cid, event) in updates.drain(..) {
770            for (plugin_id, cached_events) in self.cached_events_for_pending_plugins.iter_mut() {
771                if pid.is_none() || pid.as_ref() == Some(plugin_id) {
772                    cached_events.push(EventOrPipeMessage::Event(event.clone()));
773                }
774            }
775        }
776        Ok(())
777    }
778    pub fn get_plugin_cwd(&self, plugin_id: PluginId, client_id: ClientId) -> Option<PathBuf> {
779        self.plugin_map
780            .lock()
781            .unwrap()
782            .running_plugins()
783            .iter()
784            .find_map(|(p_id, c_id, running_plugin)| {
785                if p_id == &plugin_id && c_id == &client_id {
786                    let plugin_cwd = running_plugin
787                        .lock()
788                        .unwrap()
789                        .store
790                        .data()
791                        .plugin_cwd
792                        .clone();
793                    Some(plugin_cwd)
794                } else {
795                    None
796                }
797            })
798    }
799    pub fn change_plugin_host_dir(
800        &mut self,
801        new_host_dir: PathBuf,
802        plugin_id_to_update: PluginId,
803        client_id_to_update: ClientId,
804    ) -> Result<()> {
805        let plugins_to_change: Vec<(
806            PluginId,
807            ClientId,
808            Arc<Mutex<RunningPlugin>>,
809            Arc<Mutex<Subscriptions>>,
810        )> = self
811            .plugin_map
812            .lock()
813            .unwrap()
814            .running_plugins_and_subscriptions()
815            .iter()
816            .cloned()
817            .collect();
818        task::spawn({
819            let senders = self.senders.clone();
820            async move {
821                match new_host_dir.try_exists() {
822                    Ok(false) => {
823                        log::error!(
824                            "Failed to change folder to {},: folder does not exist",
825                            new_host_dir.display()
826                        );
827                        let _ = senders.send_to_plugin(PluginInstruction::Update(vec![(
828                            Some(plugin_id_to_update),
829                            Some(client_id_to_update),
830                            Event::FailedToChangeHostFolder(Some(format!(
831                                "Folder {} does not exist",
832                                new_host_dir.display()
833                            ))),
834                        )]));
835                        return;
836                    },
837                    Err(e) => {
838                        log::error!(
839                            "Failed to change folder to {},: {}",
840                            new_host_dir.display(),
841                            e
842                        );
843                        let _ = senders.send_to_plugin(PluginInstruction::Update(vec![(
844                            Some(plugin_id_to_update),
845                            Some(client_id_to_update),
846                            Event::FailedToChangeHostFolder(Some(e.to_string())),
847                        )]));
848                        return;
849                    },
850                    _ => {},
851                }
852                for (plugin_id, client_id, running_plugin, _subscriptions) in &plugins_to_change {
853                    if plugin_id == &plugin_id_to_update && client_id == &client_id_to_update {
854                        let mut running_plugin = running_plugin.lock().unwrap();
855                        let plugin_env = running_plugin.store.data_mut();
856                        let stdin_pipe = plugin_env.stdin_pipe.clone();
857                        let stdout_pipe = plugin_env.stdout_pipe.clone();
858                        let wasi_ctx = PluginLoader::create_wasi_ctx(
859                            &new_host_dir,
860                            &plugin_env.plugin_own_data_dir,
861                            &plugin_env.plugin_own_cache_dir,
862                            &ZELLIJ_TMP_DIR,
863                            &plugin_env.plugin.location.to_string(),
864                            plugin_env.plugin_id,
865                            stdin_pipe.clone(),
866                            stdout_pipe.clone(),
867                        );
868                        match wasi_ctx {
869                            Ok(wasi_ctx) => {
870                                drop(std::mem::replace(&mut plugin_env.wasi_ctx, wasi_ctx));
871                                plugin_env.plugin_cwd = new_host_dir.clone();
872
873                                let _ = senders.send_to_plugin(PluginInstruction::Update(vec![(
874                                    Some(*plugin_id),
875                                    Some(*client_id),
876                                    Event::HostFolderChanged(new_host_dir.clone()),
877                                )]));
878                            },
879                            Err(e) => {
880                                let _ = senders.send_to_plugin(PluginInstruction::Update(vec![(
881                                    Some(*plugin_id),
882                                    Some(*client_id),
883                                    Event::FailedToChangeHostFolder(Some(e.to_string())),
884                                )]));
885                                log::error!("Failed to create wasi ctx: {}", e);
886                            },
887                        }
888                    }
889                }
890            }
891        });
892        Ok(())
893    }
894    pub fn pipe_messages(
895        &mut self,
896        mut messages: Vec<(Option<PluginId>, Option<ClientId>, PipeMessage)>,
897        shutdown_sender: Sender<()>,
898    ) -> Result<()> {
899        let plugins_to_update: Vec<(
900            PluginId,
901            ClientId,
902            Arc<Mutex<RunningPlugin>>,
903            Arc<Mutex<Subscriptions>>,
904        )> = self
905            .plugin_map
906            .lock()
907            .unwrap()
908            .running_plugins_and_subscriptions()
909            .iter()
910            .cloned()
911            .filter(|(plugin_id, _client_id, _running_plugin, _subscriptions)| {
912                !&self
913                    .cached_events_for_pending_plugins
914                    .contains_key(&plugin_id)
915            })
916            .collect();
917        for (message_pid, message_cid, pipe_message) in messages.drain(..) {
918            for (plugin_id, client_id, running_plugin, _subscriptions) in &plugins_to_update {
919                if Self::message_is_directed_at_plugin(
920                    message_pid,
921                    message_cid,
922                    plugin_id,
923                    client_id,
924                ) {
925                    if let PipeSource::Cli(pipe_id) = &pipe_message.source {
926                        self.pending_pipes
927                            .mark_being_processed(pipe_id, plugin_id, client_id);
928                    }
929                    task::spawn({
930                        let senders = self.senders.clone();
931                        let running_plugin = running_plugin.clone();
932                        let pipe_message = pipe_message.clone();
933                        let plugin_id = *plugin_id;
934                        let client_id = *client_id;
935                        let _s = shutdown_sender.clone();
936                        async move {
937                            let mut running_plugin = running_plugin.lock().unwrap();
938                            let mut plugin_render_assets = vec![];
939                            let _s = _s; // guard to allow the task to complete before cleanup/shutdown
940                            match apply_pipe_message_to_plugin(
941                                plugin_id,
942                                client_id,
943                                &mut running_plugin,
944                                &pipe_message,
945                                &mut plugin_render_assets,
946                                &senders,
947                            ) {
948                                Ok(()) => {
949                                    let _ = senders.send_to_screen(ScreenInstruction::PluginBytes(
950                                        plugin_render_assets,
951                                    ));
952                                },
953                                Err(e) => {
954                                    log::error!("{:?}", e);
955
956                                    // https://stackoverflow.com/questions/66450942/in-rust-is-there-a-way-to-make-literal-newlines-in-r-using-windows-c
957                                    let stringified_error =
958                                        format!("{:?}", e).replace("\n", "\n\r");
959
960                                    handle_plugin_crash(
961                                        plugin_id,
962                                        stringified_error,
963                                        senders.clone(),
964                                    );
965                                },
966                            }
967                        }
968                    });
969                }
970            }
971            let all_connected_clients: Vec<ClientId> = self
972                .connected_clients
973                .lock()
974                .unwrap()
975                .iter()
976                .copied()
977                .collect();
978            for (plugin_id, cached_events) in self.cached_events_for_pending_plugins.iter_mut() {
979                if message_pid.is_none() || message_pid.as_ref() == Some(plugin_id) {
980                    cached_events.push(EventOrPipeMessage::PipeMessage(pipe_message.clone()));
981                    if let PipeSource::Cli(pipe_id) = &pipe_message.source {
982                        for client_id in &all_connected_clients {
983                            if Self::message_is_directed_at_plugin(
984                                message_pid,
985                                message_cid,
986                                plugin_id,
987                                client_id,
988                            ) {
989                                self.pending_pipes
990                                    .mark_being_processed(pipe_id, plugin_id, client_id);
991                            }
992                        }
993                    }
994                }
995            }
996        }
997        Ok(())
998    }
999    pub fn apply_cached_events(
1000        &mut self,
1001        plugin_ids: Vec<PluginId>,
1002        done_receiving_permissions: bool,
1003        shutdown_sender: Sender<()>,
1004    ) -> Result<()> {
1005        let mut applied_plugin_paths = HashSet::new();
1006        for plugin_id in plugin_ids {
1007            if !done_receiving_permissions
1008                && self
1009                    .plugin_ids_waiting_for_permission_request
1010                    .contains(&plugin_id)
1011            {
1012                continue;
1013            }
1014            self.plugin_ids_waiting_for_permission_request
1015                .remove(&plugin_id);
1016            self.apply_cached_events_and_resizes_for_plugin(plugin_id, shutdown_sender.clone())?;
1017            if let Some(run_plugin) = self.run_plugin_of_loading_plugin_id(plugin_id) {
1018                applied_plugin_paths.insert(run_plugin.clone());
1019            }
1020            self.loading_plugins
1021                .retain(|(p_id, _run_plugin), _| p_id != &plugin_id);
1022            self.clear_plugin_map_cache();
1023        }
1024        for run_plugin in applied_plugin_paths.drain() {
1025            if self.pending_plugin_reloads.remove(&run_plugin) {
1026                let _ = self.reload_plugin(&run_plugin);
1027            }
1028        }
1029        Ok(())
1030    }
1031    pub fn remove_client(&mut self, client_id: ClientId) {
1032        self.connected_clients
1033            .lock()
1034            .unwrap()
1035            .retain(|c| c != &client_id);
1036    }
1037    pub fn cleanup(&mut self) {
1038        for (_plugin_id, loading_plugin_task) in self.loading_plugins.drain() {
1039            drop(loading_plugin_task.cancel());
1040        }
1041        let plugin_ids = self.plugin_map.lock().unwrap().plugin_ids();
1042        for plugin_id in &plugin_ids {
1043            drop(self.unload_plugin(*plugin_id));
1044        }
1045        if let Some(watcher) = self.watcher.take() {
1046            watcher.stop_nonblocking();
1047        }
1048    }
1049    pub fn run_plugin_of_loading_plugin_id(&self, plugin_id: PluginId) -> Option<&RunPlugin> {
1050        self.loading_plugins
1051            .iter()
1052            .find(|((p_id, _run_plugin), _)| p_id == &plugin_id)
1053            .map(|((_p_id, run_plugin), _)| run_plugin)
1054    }
1055    pub fn run_plugin_of_plugin_id(&self, plugin_id: PluginId) -> Option<RunPlugin> {
1056        self.plugin_map
1057            .lock()
1058            .unwrap()
1059            .run_plugin_of_plugin_id(plugin_id)
1060    }
1061
1062    pub fn reconfigure(
1063        &mut self,
1064        client_id: ClientId,
1065        keybinds: Option<Keybinds>,
1066        default_mode: Option<InputMode>,
1067        default_shell: Option<TerminalAction>,
1068    ) -> Result<()> {
1069        let plugins_to_reconfigure: Vec<Arc<Mutex<RunningPlugin>>> = self
1070            .plugin_map
1071            .lock()
1072            .unwrap()
1073            .running_plugins()
1074            .iter()
1075            .cloned()
1076            .filter_map(|(_plugin_id, c_id, running_plugin)| {
1077                if c_id == client_id {
1078                    Some(running_plugin.clone())
1079                } else {
1080                    None
1081                }
1082            })
1083            .collect();
1084        if let Some(default_mode) = default_mode.as_ref() {
1085            self.base_modes.insert(client_id, *default_mode);
1086        }
1087        if let Some(keybinds) = keybinds.as_ref() {
1088            self.keybinds.insert(client_id, keybinds.clone());
1089        }
1090        self.default_shell = default_shell.clone();
1091        for running_plugin in plugins_to_reconfigure {
1092            task::spawn({
1093                let running_plugin = running_plugin.clone();
1094                let keybinds = keybinds.clone();
1095                let default_shell = default_shell.clone();
1096                async move {
1097                    let mut running_plugin = running_plugin.lock().unwrap();
1098                    if let Some(keybinds) = keybinds {
1099                        running_plugin.update_keybinds(keybinds);
1100                    }
1101                    if let Some(default_mode) = default_mode {
1102                        running_plugin.update_default_mode(default_mode);
1103                    }
1104                    running_plugin.update_default_shell(default_shell);
1105                }
1106            });
1107        }
1108        Ok(())
1109    }
1110    fn apply_cached_events_and_resizes_for_plugin(
1111        &mut self,
1112        plugin_id: PluginId,
1113        shutdown_sender: Sender<()>,
1114    ) -> Result<()> {
1115        let err_context = || format!("Failed to apply cached events to plugin");
1116        if let Some(events_or_pipe_messages) =
1117            self.cached_events_for_pending_plugins.remove(&plugin_id)
1118        {
1119            let all_connected_clients: Vec<ClientId> = self
1120                .connected_clients
1121                .lock()
1122                .unwrap()
1123                .iter()
1124                .copied()
1125                .collect();
1126            for client_id in &all_connected_clients {
1127                if let Some((running_plugin, subscriptions)) = self
1128                    .plugin_map
1129                    .lock()
1130                    .unwrap()
1131                    .get_running_plugin_and_subscriptions(plugin_id, *client_id)
1132                {
1133                    task::spawn({
1134                        let senders = self.senders.clone();
1135                        let running_plugin = running_plugin.clone();
1136                        let client_id = *client_id;
1137                        let _s = shutdown_sender.clone();
1138                        let events_or_pipe_messages = events_or_pipe_messages.clone();
1139                        async move {
1140                            let subs = subscriptions.lock().unwrap().clone();
1141                            let _s = _s; // guard to allow the task to complete before cleanup/shutdown
1142                            for event_or_pipe_message in events_or_pipe_messages {
1143                                match event_or_pipe_message {
1144                                    EventOrPipeMessage::Event(event) => {
1145                                        match EventType::from_str(&event.to_string())
1146                                            .with_context(err_context)
1147                                        {
1148                                            Ok(event_type) => {
1149                                                if !subs.contains(&event_type) {
1150                                                    continue;
1151                                                }
1152                                                let mut running_plugin =
1153                                                    running_plugin.lock().unwrap();
1154                                                let mut plugin_render_assets = vec![];
1155                                                match apply_event_to_plugin(
1156                                                    plugin_id,
1157                                                    client_id,
1158                                                    &mut running_plugin,
1159                                                    &event,
1160                                                    &mut plugin_render_assets,
1161                                                    senders.clone(),
1162                                                ) {
1163                                                    Ok(()) => {
1164                                                        let _ = senders.send_to_screen(
1165                                                            ScreenInstruction::PluginBytes(
1166                                                                plugin_render_assets,
1167                                                            ),
1168                                                        );
1169                                                    },
1170                                                    Err(e) => {
1171                                                        log::error!("{}", e);
1172                                                    },
1173                                                }
1174                                            },
1175                                            Err(e) => {
1176                                                log::error!("Failed to apply event: {:?}", e);
1177                                            },
1178                                        }
1179                                    },
1180                                    EventOrPipeMessage::PipeMessage(pipe_message) => {
1181                                        let mut running_plugin = running_plugin.lock().unwrap();
1182                                        let mut plugin_render_assets = vec![];
1183
1184                                        match apply_pipe_message_to_plugin(
1185                                            plugin_id,
1186                                            client_id,
1187                                            &mut running_plugin,
1188                                            &pipe_message,
1189                                            &mut plugin_render_assets,
1190                                            &senders,
1191                                        ) {
1192                                            Ok(()) => {
1193                                                let _ = senders.send_to_screen(
1194                                                    ScreenInstruction::PluginBytes(
1195                                                        plugin_render_assets,
1196                                                    ),
1197                                                );
1198                                            },
1199                                            Err(e) => {
1200                                                log::error!("{:?}", e);
1201
1202                                                // https://stackoverflow.com/questions/66450942/in-rust-is-there-a-way-to-make-literal-newlines-in-r-using-windows-c
1203                                                let stringified_error =
1204                                                    format!("{:?}", e).replace("\n", "\n\r");
1205
1206                                                handle_plugin_crash(
1207                                                    plugin_id,
1208                                                    stringified_error,
1209                                                    senders.clone(),
1210                                                );
1211                                            },
1212                                        }
1213                                    },
1214                                }
1215                            }
1216                        }
1217                    });
1218                }
1219            }
1220        }
1221        if let Some((rows, columns)) = self.cached_resizes_for_pending_plugins.remove(&plugin_id) {
1222            self.resize_plugin(plugin_id, columns, rows, shutdown_sender.clone())?;
1223        }
1224        self.apply_cached_worker_messages(plugin_id)?;
1225        Ok(())
1226    }
1227    pub fn apply_cached_worker_messages(&mut self, plugin_id: PluginId) -> Result<()> {
1228        if let Some(mut messages) = self.cached_worker_messages.remove(&plugin_id) {
1229            let mut worker_messages: HashMap<(ClientId, String), Vec<(String, String)>> =
1230                HashMap::new();
1231            for (client_id, worker_name, message, payload) in messages.drain(..) {
1232                worker_messages
1233                    .entry((client_id, worker_name))
1234                    .or_default()
1235                    .push((message, payload));
1236            }
1237            for ((client_id, worker_name), messages) in worker_messages.drain() {
1238                self.post_messages_to_plugin_worker(plugin_id, client_id, worker_name, messages)?;
1239            }
1240        }
1241        Ok(())
1242    }
1243    fn plugin_is_currently_being_loaded(&self, plugin_location: &RunPluginLocation) -> bool {
1244        self.loading_plugins
1245            .iter()
1246            .find(|((_plugin_id, run_plugin), _)| &run_plugin.location == plugin_location)
1247            .is_some()
1248    }
1249    fn plugin_id_of_loading_plugin(
1250        &self,
1251        plugin_location: &RunPluginLocation,
1252        plugin_configuration: &PluginUserConfiguration,
1253    ) -> Option<PluginId> {
1254        self.loading_plugins
1255            .iter()
1256            .find_map(|((plugin_id, run_plugin), _)| {
1257                if &run_plugin.location == plugin_location
1258                    && &run_plugin.configuration == plugin_configuration
1259                {
1260                    Some(*plugin_id)
1261                } else {
1262                    None
1263                }
1264            })
1265    }
1266    fn all_plugin_ids_for_plugin_location(
1267        &self,
1268        plugin_location: &RunPluginLocation,
1269        plugin_configuration: &PluginUserConfiguration,
1270    ) -> Result<Vec<PluginId>> {
1271        self.plugin_map
1272            .lock()
1273            .unwrap()
1274            .all_plugin_ids_for_plugin_location(plugin_location, plugin_configuration)
1275    }
1276    pub fn all_plugin_and_client_ids_for_plugin_location(
1277        &mut self,
1278        plugin_location: &RunPluginLocation,
1279        plugin_configuration: &PluginUserConfiguration,
1280    ) -> Vec<(PluginId, Option<ClientId>)> {
1281        if self.cached_plugin_map.is_empty() {
1282            self.cached_plugin_map = self.plugin_map.lock().unwrap().clone_plugin_assets();
1283        }
1284        match self
1285            .cached_plugin_map
1286            .get(plugin_location)
1287            .and_then(|m| m.get(plugin_configuration))
1288        {
1289            Some(plugin_and_client_ids) => plugin_and_client_ids
1290                .iter()
1291                .map(|(plugin_id, client_id)| (*plugin_id, Some(*client_id)))
1292                .collect(),
1293            None => vec![],
1294        }
1295    }
1296    pub fn all_plugin_ids(&self) -> Vec<(PluginId, ClientId)> {
1297        self.plugin_map.lock().unwrap().all_plugin_ids()
1298    }
1299    fn size_of_plugin_id(&self, plugin_id: PluginId) -> Option<(usize, usize)> {
1300        // (rows/colums)
1301        self.plugin_map
1302            .lock()
1303            .unwrap()
1304            .get_running_plugin(plugin_id, None)
1305            .map(|r| {
1306                let r = r.lock().unwrap();
1307                (r.rows, r.columns)
1308            })
1309    }
1310    fn start_plugin_loading_indication(
1311        &self,
1312        plugin_ids: &[PluginId],
1313        loading_indication: &LoadingIndication,
1314    ) {
1315        for plugin_id in plugin_ids {
1316            let _ = self
1317                .senders
1318                .send_to_screen(ScreenInstruction::StartPluginLoadingIndication(
1319                    *plugin_id,
1320                    loading_indication.clone(),
1321                ));
1322            let _ = self
1323                .senders
1324                .send_to_background_jobs(BackgroundJob::AnimatePluginLoading(*plugin_id));
1325        }
1326    }
1327    pub fn post_messages_to_plugin_worker(
1328        &mut self,
1329        plugin_id: PluginId,
1330        client_id: ClientId,
1331        worker_name: String,
1332        mut messages: Vec<(String, String)>,
1333    ) -> Result<()> {
1334        let worker =
1335            self.plugin_map
1336                .lock()
1337                .unwrap()
1338                .worker_sender(plugin_id, client_id, &worker_name);
1339        match worker {
1340            Some(worker) => {
1341                for (message, payload) in messages.drain(..) {
1342                    if let Err(e) = worker.try_send(MessageToWorker::Message(message, payload)) {
1343                        log::error!("Failed to send message to worker: {:?}", e);
1344                    }
1345                }
1346            },
1347            None => {
1348                log::warn!("Worker {worker_name} not found, caching messages");
1349                for (message, payload) in messages.drain(..) {
1350                    self.cached_worker_messages
1351                        .entry(plugin_id)
1352                        .or_default()
1353                        .push((client_id, worker_name.clone(), message, payload));
1354                }
1355            },
1356        }
1357        Ok(())
1358    }
1359    pub fn start_fs_watcher_if_not_started(&mut self) {
1360        if self.watcher.is_none() {
1361            self.watcher = match watch_filesystem(self.senders.clone(), &self.zellij_cwd) {
1362                Ok(watcher) => Some(watcher),
1363                Err(e) => {
1364                    log::error!("Failed to watch filesystem: {:?}", e);
1365                    None
1366                },
1367            };
1368        }
1369    }
1370    pub fn cache_plugin_permissions(
1371        &mut self,
1372        plugin_id: PluginId,
1373        client_id: Option<ClientId>,
1374        permissions: Vec<PermissionType>,
1375        status: PermissionStatus,
1376        cache_path: Option<PathBuf>,
1377    ) -> Result<()> {
1378        let err_context = || format!("Failed to write plugin permission {plugin_id}");
1379
1380        let running_plugin = self
1381            .plugin_map
1382            .lock()
1383            .unwrap()
1384            .get_running_plugin(plugin_id, client_id)
1385            .ok_or_else(|| anyhow!("Failed to get running plugin"))?;
1386
1387        let mut running_plugin = running_plugin.lock().unwrap();
1388
1389        let permissions = if status == PermissionStatus::Granted {
1390            permissions
1391        } else {
1392            vec![]
1393        };
1394
1395        running_plugin
1396            .store
1397            .data_mut()
1398            .set_permissions(HashSet::from_iter(permissions.clone()));
1399
1400        let mut permission_cache = PermissionCache::from_path_or_default(cache_path);
1401        permission_cache.cache(
1402            running_plugin.store.data().plugin.location.to_string(),
1403            permissions,
1404        );
1405
1406        permission_cache.write_to_file().with_context(err_context)
1407    }
1408    pub fn cache_plugin_events(&mut self, plugin_id: PluginId) {
1409        self.plugin_ids_waiting_for_permission_request
1410            .insert(plugin_id);
1411        self.cached_events_for_pending_plugins
1412            .entry(plugin_id)
1413            .or_insert_with(Default::default);
1414    }
1415
1416    // gets all running plugins details matching this run_plugin, if none are running, loads one and
1417    // returns its details
1418    pub fn get_or_load_plugins(
1419        &mut self,
1420        run_plugin_or_alias: RunPluginOrAlias,
1421        size: Size,
1422        cwd: Option<PathBuf>,
1423        skip_cache: bool,
1424        should_float: bool,
1425        should_be_open_in_place: bool,
1426        pane_title: Option<String>,
1427        pane_id_to_replace: Option<PaneId>,
1428        cli_client_id: Option<ClientId>,
1429        floating_pane_coordinates: Option<FloatingPaneCoordinates>,
1430        should_focus: bool,
1431    ) -> Vec<(PluginId, Option<ClientId>)> {
1432        let run_plugin = run_plugin_or_alias.get_run_plugin();
1433        match run_plugin {
1434            Some(run_plugin) => {
1435                let all_plugin_ids = self.all_plugin_and_client_ids_for_plugin_location(
1436                    &run_plugin.location,
1437                    &run_plugin.configuration,
1438                );
1439                if all_plugin_ids.is_empty() {
1440                    if let Some(loading_plugin_id) = self.plugin_id_of_loading_plugin(
1441                        &run_plugin.location,
1442                        &run_plugin.configuration,
1443                    ) {
1444                        return vec![(loading_plugin_id, None)];
1445                    }
1446                    match self.load_plugin(
1447                        &Some(run_plugin),
1448                        None,
1449                        size,
1450                        cwd.clone(),
1451                        skip_cache,
1452                        None,
1453                        cli_client_id,
1454                    ) {
1455                        Ok((plugin_id, client_id)) => {
1456                            let start_suppressed = false;
1457                            drop(self.senders.send_to_screen(ScreenInstruction::AddPlugin(
1458                                Some(should_float),
1459                                should_be_open_in_place,
1460                                run_plugin_or_alias,
1461                                pane_title,
1462                                None,
1463                                plugin_id,
1464                                pane_id_to_replace,
1465                                cwd,
1466                                start_suppressed,
1467                                floating_pane_coordinates,
1468                                Some(should_focus),
1469                                Some(client_id),
1470                            )));
1471                            vec![(plugin_id, Some(client_id))]
1472                        },
1473                        Err(e) => {
1474                            log::error!("Failed to load plugin: {e}");
1475                            if let Some(cli_client_id) = cli_client_id {
1476                                let _ = self.senders.send_to_server(ServerInstruction::LogError(
1477                                    vec![format!("Failed to log plugin: {e}")],
1478                                    cli_client_id,
1479                                ));
1480                            }
1481                            vec![]
1482                        },
1483                    }
1484                } else {
1485                    all_plugin_ids
1486                }
1487            },
1488            None => {
1489                log::error!("Plugin not found for alias");
1490                vec![]
1491            },
1492        }
1493    }
1494    pub fn clear_plugin_map_cache(&mut self) {
1495        self.cached_plugin_map.clear();
1496    }
1497    // returns the pipe names to unblock
1498    pub fn update_cli_pipe_state(
1499        &mut self,
1500        pipe_state_changes: Vec<PluginRenderAsset>,
1501    ) -> Vec<String> {
1502        let mut pipe_names_to_unblock = vec![];
1503        for pipe_state_change in pipe_state_changes {
1504            let client_id = pipe_state_change.client_id;
1505            let plugin_id = pipe_state_change.plugin_id;
1506            for (cli_pipe_name, pipe_state_change) in pipe_state_change.cli_pipes {
1507                pipe_names_to_unblock.append(&mut self.pending_pipes.update_pipe_state_change(
1508                    &cli_pipe_name,
1509                    pipe_state_change,
1510                    &plugin_id,
1511                    &client_id,
1512                ));
1513            }
1514        }
1515        let pipe_names_to_unblock =
1516            pipe_names_to_unblock
1517                .into_iter()
1518                .fold(HashSet::new(), |mut acc, p| {
1519                    acc.insert(p);
1520                    acc
1521                });
1522        pipe_names_to_unblock.into_iter().collect()
1523    }
1524    fn message_is_directed_at_plugin(
1525        message_pid: Option<PluginId>,
1526        message_cid: Option<ClientId>,
1527        plugin_id: &PluginId,
1528        client_id: &ClientId,
1529    ) -> bool {
1530        message_pid.is_none() && message_cid.is_none()
1531            || (message_pid.is_none() && message_cid == Some(*client_id))
1532            || (message_cid.is_none() && message_pid == Some(*plugin_id))
1533            || (message_cid == Some(*client_id) && message_pid == Some(*plugin_id))
1534    }
1535    pub fn client_is_connected(&self, client_id: &ClientId) -> bool {
1536        self.connected_clients.lock().unwrap().contains(client_id)
1537    }
1538    pub fn get_first_client_id(&self) -> Option<ClientId> {
1539        self.connected_clients
1540            .lock()
1541            .unwrap()
1542            .iter()
1543            .next()
1544            .copied()
1545    }
1546}
1547
1548fn handle_plugin_successful_loading(
1549    senders: &ThreadSenders,
1550    plugin_id: PluginId,
1551    plugin_list: BTreeMap<PluginId, RunPlugin>,
1552) {
1553    let _ = senders.send_to_background_jobs(BackgroundJob::StopPluginLoadingAnimation(plugin_id));
1554    let _ = senders.send_to_screen(ScreenInstruction::RequestStateUpdateForPlugins);
1555    let _ = senders.send_to_background_jobs(BackgroundJob::ReportPluginList(plugin_list));
1556}
1557
1558fn handle_plugin_loading_failure(
1559    senders: &ThreadSenders,
1560    plugin_id: PluginId,
1561    loading_indication: &mut LoadingIndication,
1562    error: impl std::fmt::Debug,
1563    cli_client_id: Option<ClientId>,
1564) {
1565    log::error!("{:?}", error);
1566    let _ = senders.send_to_background_jobs(BackgroundJob::StopPluginLoadingAnimation(plugin_id));
1567    loading_indication.indicate_loading_error(format!("{:?}", error));
1568    let _ = senders.send_to_screen(ScreenInstruction::UpdatePluginLoadingStage(
1569        plugin_id,
1570        loading_indication.clone(),
1571    ));
1572    if let Some(cli_client_id) = cli_client_id {
1573        let _ = senders.send_to_server(ServerInstruction::LogError(
1574            vec![format!("{:?}", error)],
1575            cli_client_id,
1576        ));
1577    }
1578}
1579
1580// TODO: move to permissions?
1581fn check_event_permission(
1582    plugin_env: &PluginEnv,
1583    event: &Event,
1584) -> (PermissionStatus, Option<PermissionType>) {
1585    if plugin_env.plugin.is_builtin() {
1586        // built-in plugins can do all the things because they're part of the application and
1587        // there's no use to deny them anything
1588        return (PermissionStatus::Granted, None);
1589    }
1590    let permission = match event {
1591        Event::ModeUpdate(..)
1592        | Event::TabUpdate(..)
1593        | Event::PaneUpdate(..)
1594        | Event::SessionUpdate(..)
1595        | Event::CopyToClipboard(..)
1596        | Event::SystemClipboardFailure
1597        | Event::CommandPaneOpened(..)
1598        | Event::CommandPaneExited(..)
1599        | Event::PaneClosed(..)
1600        | Event::EditPaneOpened(..)
1601        | Event::EditPaneExited(..)
1602        | Event::FailedToWriteConfigToDisk(..)
1603        | Event::CommandPaneReRun(..)
1604        | Event::InputReceived => PermissionType::ReadApplicationState,
1605        Event::WebServerStatus(..) => PermissionType::StartWebServer,
1606        _ => return (PermissionStatus::Granted, None),
1607    };
1608
1609    if let Some(permissions) = plugin_env.permissions.lock().unwrap().as_ref() {
1610        if permissions.contains(&permission) {
1611            return (PermissionStatus::Granted, None);
1612        }
1613    }
1614
1615    (PermissionStatus::Denied, Some(permission))
1616}
1617
1618pub fn apply_event_to_plugin(
1619    plugin_id: PluginId,
1620    client_id: ClientId,
1621    running_plugin: &mut RunningPlugin,
1622    event: &Event,
1623    plugin_render_assets: &mut Vec<PluginRenderAsset>,
1624    senders: ThreadSenders,
1625) -> Result<()> {
1626    let instance = &running_plugin.instance;
1627    let rows = running_plugin.rows;
1628    let columns = running_plugin.columns;
1629
1630    let err_context = || format!("Failed to apply event to plugin {plugin_id}");
1631    match check_event_permission(running_plugin.store.data(), event) {
1632        (PermissionStatus::Granted, _) => {
1633            let mut event = event.clone();
1634            if let Event::ModeUpdate(mode_info) = &mut event {
1635                // we do this because there can be some cases where this event arrives here with
1636                // the wrong keybindings or default mode (for example: when triggered from the CLI,
1637                // where we do not know the target client_id and thus don't know if their keybindings are the
1638                // default or if they have changed at runtime), the keybindings in running_plugin
1639                // should always be up-to-date. Ideally, we would have changed the keybindings in
1640                // ModeInfo to an Option, but alas - this is already part of our contract and that
1641                // would be a breaking change.
1642                mode_info.keybinds = running_plugin.store.data().keybinds.to_keybinds_vec();
1643                mode_info.base_mode = Some(running_plugin.store.data().default_mode);
1644            }
1645            let protobuf_event: ProtobufEvent = event
1646                .clone()
1647                .try_into()
1648                .map_err(|e| anyhow!("Failed to convert to protobuf: {:?}", e))?;
1649            let update = instance
1650                .get_typed_func::<(), i32>(&mut running_plugin.store, "update")
1651                .with_context(err_context)?;
1652            wasi_write_object(running_plugin.store.data(), &protobuf_event.encode_to_vec())
1653                .with_context(err_context)?;
1654            let should_render = update
1655                .call(&mut running_plugin.store, ())
1656                .with_context(err_context)?;
1657            let mut should_render = should_render == 1;
1658            if let Event::PermissionRequestResult(..) = event {
1659                // we always render in this case, otherwise the request permission screen stays on
1660                // screen
1661                should_render = true;
1662            }
1663            if rows > 0 && columns > 0 && should_render {
1664                let rendered_bytes = instance
1665                    .get_typed_func::<(i32, i32), ()>(&mut running_plugin.store, "render")
1666                    .and_then(|render| {
1667                        render.call(&mut running_plugin.store, (rows as i32, columns as i32))
1668                    })
1669                    .and_then(|_| wasi_read_string(running_plugin.store.data()))
1670                    .with_context(err_context)?;
1671                let pipes_to_block_or_unblock = pipes_to_block_or_unblock(running_plugin, None);
1672                let plugin_render_asset = PluginRenderAsset::new(
1673                    plugin_id,
1674                    client_id,
1675                    rendered_bytes.as_bytes().to_vec(),
1676                )
1677                .with_pipes(pipes_to_block_or_unblock);
1678                plugin_render_assets.push(plugin_render_asset);
1679            } else {
1680                // This is a bit of a hack to get around the fact that plugins are allowed not to
1681                // render and still unblock CLI pipes
1682                let pipes_to_block_or_unblock = pipes_to_block_or_unblock(running_plugin, None);
1683                let plugin_render_asset = PluginRenderAsset::new(plugin_id, client_id, vec![])
1684                    .with_pipes(pipes_to_block_or_unblock);
1685                let _ = senders
1686                    .send_to_plugin(PluginInstruction::UnblockCliPipes(vec![
1687                        plugin_render_asset,
1688                    ]))
1689                    .context("failed to unblock input pipe");
1690            }
1691        },
1692        (PermissionStatus::Denied, permission) => {
1693            log::error!(
1694                "PluginId '{}' permission '{}' is not allowed - Event '{:?}' denied",
1695                plugin_id,
1696                permission
1697                    .map(|p| p.to_string())
1698                    .unwrap_or("UNKNOWN".to_owned()),
1699                EventType::from_str(&event.to_string()).with_context(err_context)?
1700            );
1701        },
1702    }
1703    Ok(())
1704}
1705
1706pub fn handle_plugin_crash(plugin_id: PluginId, message: String, senders: ThreadSenders) {
1707    let mut loading_indication = LoadingIndication::new("Panic!".to_owned());
1708    loading_indication.indicate_loading_error(message);
1709    let _ = senders.send_to_screen(ScreenInstruction::UpdatePluginLoadingStage(
1710        plugin_id,
1711        loading_indication,
1712    ));
1713}
1714
1715pub fn apply_before_close_event_to_plugin(
1716    plugin_id: PluginId,
1717    client_id: ClientId,
1718    running_plugin: &mut RunningPlugin,
1719    senders: ThreadSenders,
1720) -> Result<()> {
1721    let instance = &running_plugin.instance;
1722
1723    let err_context = || format!("Failed to apply event to plugin {plugin_id}");
1724    let event = Event::BeforeClose;
1725    let protobuf_event: ProtobufEvent = event
1726        .clone()
1727        .try_into()
1728        .map_err(|e| anyhow!("Failed to convert to protobuf: {:?}", e))?;
1729    let update = instance
1730        .get_typed_func::<(), i32>(&mut running_plugin.store, "update")
1731        .with_context(err_context)?;
1732    wasi_write_object(running_plugin.store.data(), &protobuf_event.encode_to_vec())
1733        .with_context(err_context)?;
1734    let _should_render = update
1735        .call(&mut running_plugin.store, ())
1736        .with_context(err_context)?;
1737    let pipes_to_block_or_unblock = pipes_to_block_or_unblock(running_plugin, None);
1738    let plugin_render_asset =
1739        PluginRenderAsset::new(plugin_id, client_id, vec![]).with_pipes(pipes_to_block_or_unblock);
1740    let _ = senders
1741        .send_to_plugin(PluginInstruction::UnblockCliPipes(vec![
1742            plugin_render_asset,
1743        ]))
1744        .context("failed to unblock input pipe");
1745    Ok(())
1746}