diff --git a/default-tiles/strider/src/main.rs b/default-tiles/strider/src/main.rs index 08f44707e..c2867fe49 100644 --- a/default-tiles/strider/src/main.rs +++ b/default-tiles/strider/src/main.rs @@ -10,6 +10,41 @@ register_tile!(State); impl ZellijTile for State { fn load(&mut self) { refresh_directory(self); + subscribe(&[EventType::KeyPress]); + } + + fn update(&mut self, event: Event) { + if let Event::KeyPress(key) = event { + match key { + Key::Up | Key::Char('k') => { + *self.selected_mut() = self.selected().saturating_sub(1); + } + Key::Down | Key::Char('j') => { + let next = self.selected().saturating_add(1); + *self.selected_mut() = min(self.files.len() - 1, next); + } + Key::Right | Key::Char('\n') | Key::Char('l') => { + match self.files[self.selected()].clone() { + FsEntry::Dir(p, _) => { + self.path = p; + refresh_directory(self); + } + FsEntry::File(p, _) => open_file(&p), + } + } + Key::Left | Key::Char('h') => { + self.path.pop(); + refresh_directory(self); + } + + Key::Char('.') => { + self.toggle_hidden_files(); + refresh_directory(self); + } + + _ => (), + }; + } } fn render(&mut self, rows: usize, cols: usize) { @@ -38,38 +73,6 @@ impl ZellijTile for State { } } } - - fn handle_key(&mut self, key: Key) { - match key { - Key::Up | Key::Char('k') => { - *self.selected_mut() = self.selected().saturating_sub(1); - } - Key::Down | Key::Char('j') => { - let next = self.selected().saturating_add(1); - *self.selected_mut() = min(self.files.len() - 1, next); - } - Key::Right | Key::Char('\n') | Key::Char('l') => { - match self.files[self.selected()].clone() { - FsEntry::Dir(p, _) => { - self.path = p; - refresh_directory(self); - } - FsEntry::File(p, _) => open_file(&p), - } - } - Key::Left | Key::Char('h') => { - self.path.pop(); - refresh_directory(self); - } - - Key::Char('.') => { - self.toggle_hidden_files(); - refresh_directory(self); - } - - _ => (), - }; - } } fn refresh_directory(state: &mut State) { diff --git a/src/client/tab.rs b/src/client/tab.rs index 1818672be..1a3f411de 100644 --- a/src/client/tab.rs +++ b/src/client/tab.rs @@ -1,11 +1,11 @@ //! `Tab`s holds multiple panes. It tracks their coordinates (x/y) and size, //! as well as how they should be resized -use crate::common::{AppInstruction, SenderWithContext}; +use crate::common::{input::handler::parse_keys, AppInstruction, SenderWithContext}; use crate::layout::Layout; use crate::panes::{PaneId, PositionAndSize, TerminalPane}; use crate::pty_bus::{PtyInstruction, VteEvent}; -use crate::wasm_vm::{PluginInputType, PluginInstruction}; +use crate::wasm_vm::PluginInstruction; use crate::{boundaries::Boundaries, panes::PluginPane}; use crate::{os_input_output::OsApi, utils::shared::pad_to_size}; use std::os::unix::io::RawFd; @@ -14,6 +14,7 @@ use std::{ collections::{BTreeMap, HashSet}, }; use std::{io::Write, sync::mpsc::channel}; +use zellij_tile::data::Event; const CURSOR_HEIGHT_WIDTH_RATIO: usize = 4; // this is not accurate and kind of a magic number, TODO: look into this const MIN_TERMINAL_HEIGHT: usize = 2; @@ -553,12 +554,11 @@ impl Tab { .expect("failed to drain terminal"); } Some(PaneId::Plugin(pid)) => { - self.send_plugin_instructions - .send(PluginInstruction::Input( - PluginInputType::Normal(pid), - input_bytes, - )) - .unwrap(); + for key in parse_keys(&input_bytes) { + self.send_plugin_instructions + .send(PluginInstruction::Update(Some(pid), Event::KeyPress(key))) + .unwrap() + } } _ => {} } diff --git a/src/common/errors.rs b/src/common/errors.rs index 11d13c7e5..b12a57a13 100644 --- a/src/common/errors.rs +++ b/src/common/errors.rs @@ -281,7 +281,6 @@ pub enum PluginContext { Update, Render, Input, - GlobalInput, Unload, Quit, Tabs, @@ -291,10 +290,9 @@ impl From<&PluginInstruction> for PluginContext { fn from(plugin_instruction: &PluginInstruction) -> Self { match *plugin_instruction { PluginInstruction::Load(..) => PluginContext::Load, - PluginInstruction::Update(_) => PluginContext::Update, + PluginInstruction::Update(..) => PluginContext::Update, PluginInstruction::Render(..) => PluginContext::Render, PluginInstruction::Input(..) => PluginContext::Input, - PluginInstruction::GlobalInput(_) => PluginContext::GlobalInput, PluginInstruction::Unload(_) => PluginContext::Unload, PluginInstruction::Quit => PluginContext::Quit, PluginInstruction::UpdateTabs(..) => PluginContext::Tabs, diff --git a/src/common/input/handler.rs b/src/common/input/handler.rs index 90afb64c6..4b948cbf3 100644 --- a/src/common/input/handler.rs +++ b/src/common/input/handler.rs @@ -10,8 +10,8 @@ use crate::screen::ScreenInstruction; use crate::wasm_vm::{NaughtyEventType, PluginInputType, PluginInstruction}; use crate::CommandIsExecuting; -use termion::input::TermReadEventsAndRaw; -use zellij_tile::data::{Event, Help, InputMode, Key}; +use termion::input::{TermRead, TermReadEventsAndRaw}; +use zellij_tile::data::{Help, InputMode, Key}; use super::keybinds::key_to_actions; @@ -61,19 +61,15 @@ impl InputHandler { 'input_loop: loop { //@@@ I think this should actually just iterate over stdin directly let stdin_buffer = self.os_input.read_from_stdin(); - // FIXME: Kill me someday (soon) - drop( - self.send_plugin_instructions - .send(PluginInstruction::GlobalInput(stdin_buffer.clone())), - ); for key_result in stdin_buffer.events_and_raw() { match key_result { Ok((event, raw_bytes)) => match event { termion::event::Event::Key(key) => { let key = cast_termion_key(key); + // FIXME: This is a bit of a hack to get resizing to work! drop( - self.send_plugin_instructions - .send(PluginInstruction::Update(Event::KeyPress(key))), + self.send_screen_instructions + .send(ScreenInstruction::Render), ); // FIXME this explicit break is needed because the current test // framework relies on it to not create dead threads that loop @@ -327,6 +323,10 @@ pub fn input_loop( .handle_input(); } +pub fn parse_keys(input_bytes: &[u8]) -> Vec { + input_bytes.keys().flatten().map(cast_termion_key).collect() +} + // FIXME: This is an absolutely cursed function that should be destroyed as soon // as an alternative that doesn't touch zellij-tile can be developed... fn cast_termion_key(event: termion::event::Key) -> Key { diff --git a/src/common/mod.rs b/src/common/mod.rs index 70edcea8f..b8a4c6719 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -17,6 +17,7 @@ use std::{collections::HashMap, fs}; use std::{ collections::HashSet, io::Write, + str::FromStr, sync::{Arc, Mutex}, }; @@ -40,7 +41,7 @@ use wasm_vm::{ }; use wasmer::{ChainableNamedResolver, Instance, Module, Store, Value}; use wasmer_wasi::{Pipe, WasiState}; -use zellij_tile::data::{InputMode, Key}; +use zellij_tile::data::{EventType, InputMode, Key}; #[derive(Serialize, Deserialize, Debug)] pub enum ApiCommand { @@ -549,16 +550,21 @@ pub fn start(mut os_input: Box, opts: CliArgs) { pid_tx.send(plugin_id).unwrap(); plugin_id += 1; } - PluginInstruction::Update(event) => { - for (instance, plugin_env) in plugin_map.values() { - let update = instance.exports.get_function("update").unwrap(); - - wasi_write_string( - &plugin_env.wasi_env, - &serde_json::to_string(&event).unwrap(), - ); - update.call(&[]).unwrap(); + PluginInstruction::Update(pid, event) => { + for (&i, (instance, plugin_env)) in &plugin_map { + let subs = plugin_env.subscriptions.lock().unwrap(); + // FIXME: This is very janky... Maybe I should write my own macro for Event -> EventType? + let event_type = EventType::from_str(&event.to_string()).unwrap(); + if pid.is_none() || pid == Some(i) && subs.contains(&event_type) { + let update = instance.exports.get_function("update").unwrap(); + wasi_write_string( + &plugin_env.wasi_env, + &serde_json::to_string(&event).unwrap(), + ); + update.call(&[]).unwrap(); + } } + drop(send_screen_instructions.send(ScreenInstruction::Render)); } PluginInstruction::Render(buf_tx, pid, rows, cols) => { let (instance, plugin_env) = plugin_map.get(&pid).unwrap(); @@ -587,11 +593,15 @@ pub fn start(mut os_input: Box, opts: CliArgs) { } // FIXME: Deduplicate this with the callback below! PluginInstruction::Input(input_type, input_bytes) => { - match input_type { - PluginInputType::Normal(pid) => { - let (instance, plugin_env) = plugin_map.get(&pid).unwrap(); - let handle_key = - instance.exports.get_function("handle_key").unwrap(); + if let PluginInputType::Event(event) = input_type { + for (instance, plugin_env) in plugin_map.values() { + if !plugin_env.events.contains(&event) { + continue; + } + let handle_key = instance + .exports + .get_function(handler_map.get(&event).unwrap()) + .unwrap(); for key in input_bytes.keys().flatten() { let key = cast_termion_key(key); wasi_write_string( @@ -601,45 +611,9 @@ pub fn start(mut os_input: Box, opts: CliArgs) { handle_key.call(&[]).unwrap(); } } - PluginInputType::Event(event) => { - for (instance, plugin_env) in plugin_map.values() { - if !plugin_env.events.contains(&event) { - continue; - } - let handle_key = instance - .exports - .get_function(handler_map.get(&event).unwrap()) - .unwrap(); - for key in input_bytes.keys().flatten() { - let key = cast_termion_key(key); - wasi_write_string( - &plugin_env.wasi_env, - &serde_json::to_string(&key).unwrap(), - ); - handle_key.call(&[]).unwrap(); - } - } - } } drop(send_screen_instructions.send(ScreenInstruction::Render)); } - PluginInstruction::GlobalInput(input_bytes) => { - // FIXME: Set up an event subscription system, and timed callbacks - for (instance, plugin_env) in plugin_map.values() { - let handler = - instance.exports.get_function("handle_global_key").unwrap(); - for key in input_bytes.keys().flatten() { - let key = cast_termion_key(key); - wasi_write_string( - &plugin_env.wasi_env, - &serde_json::to_string(&key).unwrap(), - ); - handler.call(&[]).unwrap(); - } - } - - drop(send_screen_instructions.send(ScreenInstruction::Render)); - } PluginInstruction::Unload(pid) => drop(plugin_map.remove(&pid)), PluginInstruction::Quit => break, } diff --git a/src/common/wasm_vm.rs b/src/common/wasm_vm.rs index 2f2a0cf68..24b947d64 100644 --- a/src/common/wasm_vm.rs +++ b/src/common/wasm_vm.rs @@ -30,10 +30,9 @@ pub enum PluginInputType { #[derive(Clone, Debug)] pub enum PluginInstruction { Load(Sender, PathBuf, Vec), - Update(Event), + Update(Option, Event), // Focused plugin / broadcast, event data Render(Sender, u32, usize, usize), // String buffer, plugin id, rows, cols - Input(PluginInputType, Vec), // plugin id, input bytes - GlobalInput(Vec), // input bytes + Input(PluginInputType, Vec), // plugin id, input bytes Unload(u32), UpdateTabs(Vec), // num tabs, active tab Quit, diff --git a/zellij-tile/src/data.rs b/zellij-tile/src/data.rs index abca9994e..a6e07983a 100644 --- a/zellij-tile/src/data.rs +++ b/zellij-tile/src/data.rs @@ -1,5 +1,5 @@ use serde::{Deserialize, Serialize}; -use strum_macros::{EnumDiscriminants, EnumIter, ToString}; +use strum_macros::{EnumDiscriminants, EnumIter, EnumString, ToString}; #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] pub enum Key { @@ -24,7 +24,7 @@ pub enum Key { } #[derive(Debug, Clone, EnumDiscriminants, ToString, Serialize, Deserialize)] -#[strum_discriminants(derive(Hash, Serialize, Deserialize))] +#[strum_discriminants(derive(EnumString, Hash, Serialize, Deserialize))] #[strum_discriminants(name(EventType))] pub enum Event { ModeUpdate(Help), // FIXME: Rename the `Help` struct diff --git a/zellij-tile/src/lib.rs b/zellij-tile/src/lib.rs index eb1b08fb7..771edb2dd 100644 --- a/zellij-tile/src/lib.rs +++ b/zellij-tile/src/lib.rs @@ -10,8 +10,6 @@ pub trait ZellijTile { fn update(&mut self, event: Event) {} fn render(&mut self, rows: usize, cols: usize) {} // FIXME: Everything below this line should be purged - fn handle_key(&mut self, key: Key) {} - fn handle_global_key(&mut self, key: Key) {} fn update_tabs(&mut self) {} fn handle_tab_rename_keypress(&mut self, key: Key) {} } @@ -45,22 +43,6 @@ macro_rules! register_tile { }); } - #[no_mangle] - pub fn handle_key() { - STATE.with(|state| { - state.borrow_mut().handle_key($crate::shim::get_key()); - }); - } - - #[no_mangle] - pub fn handle_global_key() { - STATE.with(|state| { - state - .borrow_mut() - .handle_global_key($crate::shim::get_key()); - }); - } - #[no_mangle] pub fn update_tabs() { STATE.with(|state| {