Add event subscription tracking

This commit is contained in:
Brooks J Rady 2021-03-09 21:51:17 +00:00
parent 06bce9a1fd
commit e9ab81850e
10 changed files with 85 additions and 40 deletions

View file

@ -1,29 +1,25 @@
#!/bin/sh #!/bin/sh
total=6 total=5
# This is temporary while https://github.com/rust-lang/cargo/issues/7004 is open # This is temporary while https://github.com/rust-lang/cargo/issues/7004 is open
echo "Building zellij-tile (1/$total)..." echo "Building status-bar (1/$total)..."
cd zellij-tile cd default-tiles/status-bar
cargo build --release --target-dir ../../target cargo build --release --target-dir ../../target
echo "Building status-bar (2/$total)..." echo "Building strider (2/$total)..."
cd ../default-tiles/status-bar
cargo build --release --target-dir ../../target
echo "Building strider (3/$total)..."
cd ../strider cd ../strider
cargo build --release --target-dir ../../target cargo build --release --target-dir ../../target
echo "Building tab-bar (4/$total)..." echo "Building tab-bar (3/$total)..."
cd ../tab-bar cd ../tab-bar
cargo build --release --target-dir ../../target cargo build --release --target-dir ../../target
echo "Optimising WASM executables (5/$total)..." echo "Optimising WASM executables (4/$total)..."
cd ../.. cd ../..
wasm-opt -O target/wasm32-wasi/release/status-bar.wasm -o target/status-bar.wasm || cp target/wasm32-wasi/release/status-bar.wasm target/status-bar.wasm wasm-opt -O target/wasm32-wasi/release/status-bar.wasm -o target/status-bar.wasm || cp target/wasm32-wasi/release/status-bar.wasm target/status-bar.wasm
wasm-opt -O target/wasm32-wasi/release/strider.wasm -o target/strider.wasm || cp target/wasm32-wasi/release/strider.wasm target/strider.wasm wasm-opt -O target/wasm32-wasi/release/strider.wasm -o target/strider.wasm || cp target/wasm32-wasi/release/strider.wasm target/strider.wasm
wasm-opt -O target/wasm32-wasi/release/tab-bar.wasm -o target/tab-bar.wasm || cp target/wasm32-wasi/release/tab-bar.wasm target/tab-bar.wasm wasm-opt -O target/wasm32-wasi/release/tab-bar.wasm -o target/tab-bar.wasm || cp target/wasm32-wasi/release/tab-bar.wasm target/tab-bar.wasm
echo "Building zellij (6/$total)..." echo "Building zellij (5/$total)..."
cargo build --target-dir target $@ cargo build --target-dir target $@

View file

@ -28,7 +28,7 @@ impl Default for BarMode {
struct State { struct State {
active_tab_index: usize, active_tab_index: usize,
num_tabs: usize, num_tabs: usize,
tabs: Vec<TabData>, tabs: Vec<TabInfo>,
mode: BarMode, mode: BarMode,
new_name: String, new_name: String,
} }

View file

@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::{fs::File, io::prelude::*}; use std::{fs::File, io::prelude::*};
use crate::common::wasm_vm::EventType; use crate::common::wasm_vm::NaughtyEventType;
use crate::panes::PositionAndSize; use crate::panes::PositionAndSize;
fn split_space_to_parts_vertically( fn split_space_to_parts_vertically(
@ -182,7 +182,7 @@ pub struct Layout {
#[serde(default)] #[serde(default)]
pub expansion_boundary: bool, pub expansion_boundary: bool,
#[serde(default, skip_serializing_if = "Vec::is_empty")] #[serde(default, skip_serializing_if = "Vec::is_empty")]
pub events: Vec<EventType>, pub events: Vec<NaughtyEventType>,
} }
impl Layout { impl Layout {

View file

@ -164,6 +164,7 @@ impl Display for ContextType {
} }
} }
// FIXME: Just deriving EnumDiscriminants from strum will remove the need for any of this!!!
/// Stack call representations corresponding to the different types of [`ScreenInstruction`]s. /// Stack call representations corresponding to the different types of [`ScreenInstruction`]s.
#[derive(Debug, Clone, Copy, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq)]
pub enum ScreenContext { pub enum ScreenContext {
@ -201,6 +202,7 @@ pub enum ScreenContext {
UpdateTabName, UpdateTabName,
} }
// FIXME: Just deriving EnumDiscriminants from strum will remove the need for any of this!!!
impl From<&ScreenInstruction> for ScreenContext { impl From<&ScreenInstruction> for ScreenContext {
fn from(screen_instruction: &ScreenInstruction) -> Self { fn from(screen_instruction: &ScreenInstruction) -> Self {
match *screen_instruction { match *screen_instruction {

View file

@ -7,7 +7,7 @@ use crate::errors::ContextType;
use crate::os_input_output::OsApi; use crate::os_input_output::OsApi;
use crate::pty_bus::PtyInstruction; use crate::pty_bus::PtyInstruction;
use crate::screen::ScreenInstruction; use crate::screen::ScreenInstruction;
use crate::wasm_vm::{EventType, PluginInputType, PluginInstruction}; use crate::wasm_vm::{NaughtyEventType, PluginInputType, PluginInstruction};
use crate::CommandIsExecuting; use crate::CommandIsExecuting;
use termion::input::TermReadEventsAndRaw; use termion::input::TermReadEventsAndRaw;
@ -234,7 +234,7 @@ impl InputHandler {
Action::TabNameInput(c) => { Action::TabNameInput(c) => {
self.send_plugin_instructions self.send_plugin_instructions
.send(PluginInstruction::Input( .send(PluginInstruction::Input(
PluginInputType::Event(EventType::Tab), PluginInputType::Event(NaughtyEventType::Tab),
c.clone(), c.clone(),
)) ))
.unwrap(); .unwrap();
@ -245,7 +245,7 @@ impl InputHandler {
Action::SaveTabName => { Action::SaveTabName => {
self.send_plugin_instructions self.send_plugin_instructions
.send(PluginInstruction::Input( .send(PluginInstruction::Input(
PluginInputType::Event(EventType::Tab), PluginInputType::Event(NaughtyEventType::Tab),
vec![b'\n'], vec![b'\n'],
)) ))
.unwrap(); .unwrap();

View file

@ -9,12 +9,16 @@ pub mod screen;
pub mod utils; pub mod utils;
pub mod wasm_vm; pub mod wasm_vm;
use std::io::Write;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::sync::mpsc; use std::sync::mpsc;
use std::thread; use std::thread;
use std::{cell::RefCell, sync::mpsc::TrySendError}; use std::{cell::RefCell, sync::mpsc::TrySendError};
use std::{collections::HashMap, fs}; use std::{collections::HashMap, fs};
use std::{
collections::HashSet,
io::Write,
sync::{Arc, Mutex},
};
use crate::cli::CliArgs; use crate::cli::CliArgs;
use crate::layout::Layout; use crate::layout::Layout;
@ -31,7 +35,8 @@ use termion::input::TermRead;
use utils::consts::{ZELLIJ_IPC_PIPE, ZELLIJ_ROOT_PLUGIN_DIR}; use utils::consts::{ZELLIJ_IPC_PIPE, ZELLIJ_ROOT_PLUGIN_DIR};
use wasm_vm::PluginEnv; use wasm_vm::PluginEnv;
use wasm_vm::{ use wasm_vm::{
wasi_stdout, wasi_write_string, zellij_imports, EventType, PluginInputType, PluginInstruction, wasi_stdout, wasi_write_string, zellij_imports, NaughtyEventType, PluginInputType,
PluginInstruction,
}; };
use wasmer::{ChainableNamedResolver, Instance, Module, Store, Value}; use wasmer::{ChainableNamedResolver, Instance, Module, Store, Value};
use wasmer_wasi::{Pipe, WasiState}; use wasmer_wasi::{Pipe, WasiState};
@ -442,8 +447,10 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
let store = Store::default(); let store = Store::default();
let mut plugin_id = 0; let mut plugin_id = 0;
let mut plugin_map = HashMap::new(); let mut plugin_map = HashMap::new();
let handler_map: HashMap<EventType, String> = let handler_map: HashMap<NaughtyEventType, String> = [(
[(EventType::Tab, "handle_tab_rename_keypress".to_string())] NaughtyEventType::Tab,
"handle_tab_rename_keypress".to_string(),
)]
.iter() .iter()
.cloned() .cloned()
.collect(); .collect();
@ -499,6 +506,7 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
send_screen_instructions: send_screen_instructions.clone(), send_screen_instructions: send_screen_instructions.clone(),
send_app_instructions: send_app_instructions.clone(), send_app_instructions: send_app_instructions.clone(),
wasi_env, wasi_env,
subscriptions: Arc::new(Mutex::new(HashSet::new())),
events, events,
}; };
@ -526,7 +534,7 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
} }
PluginInstruction::UpdateTabs(mut tabs) => { PluginInstruction::UpdateTabs(mut tabs) => {
for (instance, plugin_env) in plugin_map.values() { for (instance, plugin_env) in plugin_map.values() {
if !plugin_env.events.contains(&EventType::Tab) { if !plugin_env.events.contains(&NaughtyEventType::Tab) {
continue; continue;
} }
let handler = instance.exports.get_function("update_tabs").unwrap(); let handler = instance.exports.get_function("update_tabs").unwrap();

View file

@ -13,7 +13,7 @@ use crate::tab::Tab;
use crate::{errors::ErrorContext, wasm_vm::PluginInstruction}; use crate::{errors::ErrorContext, wasm_vm::PluginInstruction};
use crate::{layout::Layout, panes::PaneId}; use crate::{layout::Layout, panes::PaneId};
use zellij_tile::data::TabData; use zellij_tile::data::TabInfo;
/// Instructions that can be sent to the [`Screen`]. /// Instructions that can be sent to the [`Screen`].
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -272,7 +272,7 @@ impl Screen {
let mut tab_data = vec![]; let mut tab_data = vec![];
let active_tab_index = self.active_tab_index.unwrap(); let active_tab_index = self.active_tab_index.unwrap();
for tab in self.tabs.values() { for tab in self.tabs.values() {
tab_data.push(TabData { tab_data.push(TabInfo {
position: tab.position, position: tab.position,
name: tab.name.clone(), name: tab.name.clone(),
active: active_tab_index == tab.index, active: active_tab_index == tab.index,

View file

@ -1,11 +1,15 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::{ use std::{
collections::HashSet,
path::PathBuf, path::PathBuf,
sync::mpsc::{channel, Sender}, sync::{
mpsc::{channel, Sender},
Arc, Mutex,
},
}; };
use wasmer::{imports, Function, ImportObject, Store, WasmerEnv}; use wasmer::{imports, Function, ImportObject, Store, WasmerEnv};
use wasmer_wasi::WasiEnv; use wasmer_wasi::WasiEnv;
use zellij_tile::data::TabData; use zellij_tile::data::{EventType, TabInfo};
use super::{ use super::{
input::handler::get_help, pty_bus::PtyInstruction, screen::ScreenInstruction, AppInstruction, input::handler::get_help, pty_bus::PtyInstruction, screen::ScreenInstruction, AppInstruction,
@ -13,24 +17,24 @@ use super::{
}; };
#[derive(Clone, Debug, PartialEq, Hash, Eq, Serialize, Deserialize)] #[derive(Clone, Debug, PartialEq, Hash, Eq, Serialize, Deserialize)]
pub enum EventType { pub enum NaughtyEventType {
Tab, Tab,
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum PluginInputType { pub enum PluginInputType {
Normal(u32), Normal(u32),
Event(EventType), Event(NaughtyEventType),
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum PluginInstruction { pub enum PluginInstruction {
Load(Sender<u32>, PathBuf, Vec<EventType>), Load(Sender<u32>, PathBuf, Vec<NaughtyEventType>),
Draw(Sender<String>, u32, usize, usize), // String buffer, plugin id, rows, cols Draw(Sender<String>, u32, usize, usize), // String buffer, plugin id, rows, cols
Input(PluginInputType, Vec<u8>), // plugin id, input bytes Input(PluginInputType, Vec<u8>), // plugin id, input bytes
GlobalInput(Vec<u8>), // input bytes GlobalInput(Vec<u8>), // input bytes
Unload(u32), Unload(u32),
UpdateTabs(Vec<TabData>), // num tabs, active tab UpdateTabs(Vec<TabInfo>), // num tabs, active tab
Quit, Quit,
} }
@ -41,7 +45,8 @@ pub struct PluginEnv {
pub send_app_instructions: SenderWithContext<AppInstruction>, pub send_app_instructions: SenderWithContext<AppInstruction>,
pub send_pty_instructions: SenderWithContext<PtyInstruction>, // FIXME: This should be a big bundle of all of the channels pub send_pty_instructions: SenderWithContext<PtyInstruction>, // FIXME: This should be a big bundle of all of the channels
pub wasi_env: WasiEnv, pub wasi_env: WasiEnv,
pub events: Vec<EventType>, pub subscriptions: Arc<Mutex<HashSet<EventType>>>,
pub events: Vec<NaughtyEventType>, // FIXME: Murder this very soon (should not survive into main)
} }
// Plugin API --------------------------------------------------------------------------------------------------------- // Plugin API ---------------------------------------------------------------------------------------------------------
@ -49,6 +54,8 @@ pub struct PluginEnv {
pub fn zellij_imports(store: &Store, plugin_env: &PluginEnv) -> ImportObject { pub fn zellij_imports(store: &Store, plugin_env: &PluginEnv) -> ImportObject {
imports! { imports! {
"zellij" => { "zellij" => {
"host_subscribe" => Function::new_native_with_env(store, plugin_env.clone(), host_subscribe),
"host_unsubscribe" => Function::new_native_with_env(store, plugin_env.clone(), host_unsubscribe),
"host_open_file" => Function::new_native_with_env(store, plugin_env.clone(), host_open_file), "host_open_file" => Function::new_native_with_env(store, plugin_env.clone(), host_open_file),
"host_set_invisible_borders" => Function::new_native_with_env(store, plugin_env.clone(), host_set_invisible_borders), "host_set_invisible_borders" => Function::new_native_with_env(store, plugin_env.clone(), host_set_invisible_borders),
"host_set_max_height" => Function::new_native_with_env(store, plugin_env.clone(), host_set_max_height), "host_set_max_height" => Function::new_native_with_env(store, plugin_env.clone(), host_set_max_height),
@ -58,7 +65,18 @@ pub fn zellij_imports(store: &Store, plugin_env: &PluginEnv) -> ImportObject {
} }
} }
// FIXME: Bundle up all of the channels! Pair that with WasiEnv? fn host_subscribe(plugin_env: &PluginEnv) {
let mut subscriptions = plugin_env.subscriptions.lock().unwrap();
let new: HashSet<EventType> = serde_json::from_str(&wasi_stdout(&plugin_env.wasi_env)).unwrap();
subscriptions.extend(new);
}
fn host_unsubscribe(plugin_env: &PluginEnv) {
let mut subscriptions = plugin_env.subscriptions.lock().unwrap();
let old: HashSet<EventType> = serde_json::from_str(&wasi_stdout(&plugin_env.wasi_env)).unwrap();
subscriptions.retain(|k| !old.contains(k));
}
fn host_open_file(plugin_env: &PluginEnv) { fn host_open_file(plugin_env: &PluginEnv) {
let path = PathBuf::from(wasi_stdout(&plugin_env.wasi_env).lines().next().unwrap()); let path = PathBuf::from(wasi_stdout(&plugin_env.wasi_env).lines().next().unwrap());
plugin_env plugin_env
@ -67,7 +85,6 @@ fn host_open_file(plugin_env: &PluginEnv) {
.unwrap(); .unwrap();
} }
// FIXME: Think about these naming conventions should everything be prefixed by 'host'?
fn host_set_selectable(plugin_env: &PluginEnv, selectable: i32) { fn host_set_selectable(plugin_env: &PluginEnv, selectable: i32) {
let selectable = selectable != 0; let selectable = selectable != 0;
plugin_env plugin_env

View file

@ -1,5 +1,5 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use strum_macros::EnumIter; use strum_macros::{EnumDiscriminants, EnumIter, ToString};
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Key { pub enum Key {
@ -23,6 +23,15 @@ pub enum Key {
Esc, Esc,
} }
#[derive(Debug, EnumDiscriminants, ToString, Serialize, Deserialize)]
#[strum_discriminants(derive(Hash, Serialize, Deserialize))]
#[strum_discriminants(name(EventType))]
pub enum Event {
ModeUpdate(Help), // FIXME: Rename the `Help` struct
TabUpdate(TabInfo),
KeyPress(Key),
}
/// Describes the different input modes, which change the way that keystrokes will be interpreted. /// Describes the different input modes, which change the way that keystrokes will be interpreted.
#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone, EnumIter, Serialize, Deserialize)] #[derive(Debug, PartialEq, Eq, Hash, Copy, Clone, EnumIter, Serialize, Deserialize)]
pub enum InputMode { pub enum InputMode {
@ -56,11 +65,12 @@ impl Default for InputMode {
#[derive(Default, Debug, Clone, Serialize, Deserialize)] #[derive(Default, Debug, Clone, Serialize, Deserialize)]
pub struct Help { pub struct Help {
pub mode: InputMode, pub mode: InputMode,
// FIXME: This should probably return Keys and Actions, then sort out strings plugin-side
pub keybinds: Vec<(String, String)>, // <shortcut> => <shortcut description> pub keybinds: Vec<(String, String)>, // <shortcut> => <shortcut description>
} }
#[derive(Debug, Clone, Deserialize, Serialize)] #[derive(Debug, Default, Clone, Deserialize, Serialize)]
pub struct TabData { pub struct TabInfo {
/* subset of fields to publish to plugins */ /* subset of fields to publish to plugins */
pub position: usize, pub position: usize,
pub name: String, pub name: String,

View file

@ -7,6 +7,16 @@ pub fn get_key() -> Key {
deserialize_from_stdin().unwrap() deserialize_from_stdin().unwrap()
} }
pub fn subscribe(event_types: &[EventType]) {
println!("{}", serde_json::to_string(event_types).unwrap());
unsafe { host_subscribe() };
}
pub fn unsubscribe(event_types: &[EventType]) {
println!("{}", serde_json::to_string(event_types).unwrap());
unsafe { host_unsubscribe() };
}
pub fn open_file(path: &Path) { pub fn open_file(path: &Path) {
println!("{}", path.to_string_lossy()); println!("{}", path.to_string_lossy());
unsafe { host_open_file() }; unsafe { host_open_file() };
@ -31,7 +41,7 @@ pub fn get_help() -> Help {
deserialize_from_stdin().unwrap_or_default() deserialize_from_stdin().unwrap_or_default()
} }
pub fn get_tabs() -> Vec<TabData> { pub fn get_tabs() -> Vec<TabInfo> {
deserialize_from_stdin().unwrap_or_default() deserialize_from_stdin().unwrap_or_default()
} }
@ -43,6 +53,8 @@ fn deserialize_from_stdin<T: DeserializeOwned>() -> Option<T> {
#[link(wasm_import_module = "zellij")] #[link(wasm_import_module = "zellij")]
extern "C" { extern "C" {
fn host_subscribe();
fn host_unsubscribe();
fn host_open_file(); fn host_open_file();
fn host_set_max_height(max_height: i32); fn host_set_max_height(max_height: i32);
fn host_set_selectable(selectable: i32); fn host_set_selectable(selectable: i32);