From 2814c3027271d15b4af345bdde03591bb189cbce Mon Sep 17 00:00:00 2001 From: Brooks J Rady Date: Tue, 27 Apr 2021 14:57:54 +0100 Subject: [PATCH 1/8] feat(plugin): added the `get_plugin_ids()` query function --- Cargo.lock | 2 +- Cargo.toml | 2 +- src/common/mod.rs | 10 +++---- src/common/wasm_vm.rs | 59 ++++++++++++++++++++++++++++------------- zellij-tile/Cargo.toml | 2 +- zellij-tile/src/data.rs | 14 +++++++--- zellij-tile/src/shim.rs | 13 ++++++--- 7 files changed, 68 insertions(+), 34 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5fc7175c3..615cc385d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2217,7 +2217,7 @@ dependencies = [ [[package]] name = "zellij-tile" -version = "1.0.0" +version = "1.1.0" dependencies = [ "serde", "serde_json", diff --git a/Cargo.toml b/Cargo.toml index df330008a..4aa44e47d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,7 +36,7 @@ lazy_static = "1.4.0" wasmer = "1.0.0" wasmer-wasi = "1.0.0" interprocess = "1.0.1" -zellij-tile = { path = "zellij-tile/", version = "1.0.0" } +zellij-tile = { path = "zellij-tile/", version = "1.1.0" } [dependencies.async-std] version = "1.3.0" diff --git a/src/common/mod.rs b/src/common/mod.rs index 79e487806..d525a3fe2 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -40,8 +40,7 @@ use pty_bus::{PtyBus, PtyInstruction}; use screen::{Screen, ScreenInstruction}; use serde::{Deserialize, Serialize}; use utils::consts::ZELLIJ_IPC_PIPE; -use wasm_vm::PluginEnv; -use wasm_vm::{wasi_stdout, wasi_write_string, zellij_imports, PluginInstruction}; +use wasm_vm::{wasi_stdout, wasi_write_json, zellij_exports, PluginEnv, PluginInstruction}; use wasmer::{ChainableNamedResolver, Instance, Module, Store, Value}; use wasmer_wasi::{Pipe, WasiState}; use zellij_tile::data::{EventType, ModeInfo}; @@ -506,7 +505,7 @@ pub fn start(mut os_input: Box, opts: CliArgs) { subscriptions: Arc::new(Mutex::new(HashSet::new())), }; - let zellij = zellij_imports(&store, &plugin_env); + let zellij = zellij_exports(&store, &plugin_env); let instance = Instance::new(&module, &zellij.chain_back(wasi)).unwrap(); let start = instance.exports.get_function("_start").unwrap(); @@ -525,10 +524,7 @@ pub fn start(mut os_input: Box, opts: CliArgs) { 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(), - ); + wasi_write_json(&plugin_env.wasi_env, &event); update.call(&[]).unwrap(); } } diff --git a/src/common/wasm_vm.rs b/src/common/wasm_vm.rs index 29b110aed..ef3df61a6 100644 --- a/src/common/wasm_vm.rs +++ b/src/common/wasm_vm.rs @@ -1,11 +1,13 @@ +use serde::Serialize; use std::{ collections::HashSet, path::PathBuf, + process, sync::{mpsc::Sender, Arc, Mutex}, }; use wasmer::{imports, Function, ImportObject, Store, WasmerEnv}; use wasmer_wasi::WasiEnv; -use zellij_tile::data::{Event, EventType}; +use zellij_tile::data::{Event, EventType, PluginIds}; use super::{ pty_bus::PtyInstruction, screen::ScreenInstruction, AppInstruction, PaneId, SenderWithContext, @@ -32,17 +34,26 @@ pub struct PluginEnv { // Plugin API --------------------------------------------------------------------------------------------------------- -pub fn zellij_imports(store: &Store, plugin_env: &PluginEnv) -> ImportObject { - imports! { - "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_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_selectable" => Function::new_native_with_env(store, plugin_env.clone(), host_set_selectable), +pub fn zellij_exports(store: &Store, plugin_env: &PluginEnv) -> ImportObject { + macro_rules! zellij_export { + ($($host_function:ident),+ $(,)?) => { + imports! { + "zellij" => { + $("$host_function" => Function::new_native_with_env(store, plugin_env.clone(), $host_function),)+ + } + } } } + + zellij_export! { + host_subscribe, + host_unsubscribe, + host_set_invisible_borders, + host_set_max_height, + host_set_selectable, + host_get_plugin_ids, + host_open_file, + } } fn host_subscribe(plugin_env: &PluginEnv) { @@ -57,14 +68,6 @@ fn host_unsubscribe(plugin_env: &PluginEnv) { subscriptions.retain(|k| !old.contains(k)); } -fn host_open_file(plugin_env: &PluginEnv) { - let path = PathBuf::from(wasi_stdout(&plugin_env.wasi_env).lines().next().unwrap()); - plugin_env - .send_pty_instructions - .send(PtyInstruction::SpawnTerminal(Some(path))) - .unwrap(); -} - fn host_set_selectable(plugin_env: &PluginEnv, selectable: i32) { let selectable = selectable != 0; plugin_env @@ -98,6 +101,22 @@ fn host_set_invisible_borders(plugin_env: &PluginEnv, invisible_borders: i32) { .unwrap() } +fn host_get_plugin_ids(plugin_env: &PluginEnv) { + let ids = PluginIds { + plugin_id: plugin_env.plugin_id, + zellij_pid: process::id(), + }; + wasi_write_json(&plugin_env.wasi_env, &ids); +} + +fn host_open_file(plugin_env: &PluginEnv) { + let path = PathBuf::from(wasi_stdout(&plugin_env.wasi_env).lines().next().unwrap()); + plugin_env + .send_pty_instructions + .send(PtyInstruction::SpawnTerminal(Some(path))) + .unwrap(); +} + // Helper Functions --------------------------------------------------------------------------------------------------- // FIXME: Unwrap city @@ -114,3 +133,7 @@ pub fn wasi_write_string(wasi_env: &WasiEnv, buf: &str) { let wasi_file = state.fs.stdin_mut().unwrap().as_mut().unwrap(); writeln!(wasi_file, "{}\r", buf).unwrap(); } + +pub fn wasi_write_json(wasi_env: &WasiEnv, object: &impl Serialize) { + wasi_write_string(wasi_env, &serde_json::to_string(&object).unwrap()); +} diff --git a/zellij-tile/Cargo.toml b/zellij-tile/Cargo.toml index 5a299cb1e..f59cf7b3e 100644 --- a/zellij-tile/Cargo.toml +++ b/zellij-tile/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "zellij-tile" -version = "1.0.0" +version = "1.1.0" authors = ["Brooks J Rady "] edition = "2018" description = "A small client-side library for writing Zellij plugins" diff --git a/zellij-tile/src/data.rs b/zellij-tile/src/data.rs index 85d12245b..e7ddb319c 100644 --- a/zellij-tile/src/data.rs +++ b/zellij-tile/src/data.rs @@ -23,7 +23,9 @@ pub enum Key { Esc, } -#[derive(Debug, Clone, EnumDiscriminants, ToString, Serialize, Deserialize)] +#[derive( + Debug, Clone, PartialEq, Eq, Hash, EnumDiscriminants, ToString, Serialize, Deserialize, +)] #[strum_discriminants(derive(EnumString, Hash, Serialize, Deserialize))] #[strum_discriminants(name(EventType))] pub enum Event { @@ -68,17 +70,23 @@ impl Default for InputMode { /// Represents the contents of the help message that is printed in the status bar, /// which indicates the current [`InputMode`] and what the keybinds for that mode /// are. Related to the default `status-bar` plugin. -#[derive(Default, Debug, Clone, Serialize, Deserialize)] +#[derive(Default, Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct ModeInfo { pub mode: InputMode, // FIXME: This should probably return Keys and Actions, then sort out strings plugin-side pub keybinds: Vec<(String, String)>, // => } -#[derive(Debug, Default, Clone, Deserialize, Serialize)] +#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)] pub struct TabInfo { /* subset of fields to publish to plugins */ pub position: usize, pub name: String, pub active: bool, } + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)] +pub struct PluginIds { + pub plugin_id: u32, + pub zellij_pid: u32, +} diff --git a/zellij-tile/src/shim.rs b/zellij-tile/src/shim.rs index 42a646a1f..ca15919fe 100644 --- a/zellij-tile/src/shim.rs +++ b/zellij-tile/src/shim.rs @@ -21,12 +21,18 @@ pub fn set_max_height(max_height: i32) { unsafe { host_set_max_height(max_height) }; } +pub fn set_selectable(selectable: bool) { + unsafe { host_set_selectable(if selectable { 1 } else { 0 }) }; +} + pub fn set_invisible_borders(invisible_borders: bool) { unsafe { host_set_invisible_borders(if invisible_borders { 1 } else { 0 }) }; } -pub fn set_selectable(selectable: bool) { - unsafe { host_set_selectable(if selectable { 1 } else { 0 }) }; +// Query Functions +pub fn get_plugin_ids() -> PluginIds { + unsafe { host_get_plugin_ids() }; + object_from_stdin() } // Host Functions @@ -49,8 +55,9 @@ pub fn object_from_stdin() -> T { extern "C" { fn host_subscribe(); fn host_unsubscribe(); - fn host_open_file(); fn host_set_max_height(max_height: i32); fn host_set_selectable(selectable: i32); fn host_set_invisible_borders(invisible_borders: i32); + fn host_get_plugin_ids(); + fn host_open_file(); } From 1b36579d3b58b3bd70607cbbaa0eded52c041d1c Mon Sep 17 00:00:00 2001 From: Brooks J Rady Date: Tue, 27 Apr 2021 15:12:26 +0100 Subject: [PATCH 2/8] fix(plugin): fix bad export macro --- src/common/wasm_vm.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/common/wasm_vm.rs b/src/common/wasm_vm.rs index ef3df61a6..e8990e863 100644 --- a/src/common/wasm_vm.rs +++ b/src/common/wasm_vm.rs @@ -39,7 +39,8 @@ pub fn zellij_exports(store: &Store, plugin_env: &PluginEnv) -> ImportObject { ($($host_function:ident),+ $(,)?) => { imports! { "zellij" => { - $("$host_function" => Function::new_native_with_env(store, plugin_env.clone(), $host_function),)+ + $(stringify!($host_function) => + Function::new_native_with_env(store, plugin_env.clone(), $host_function),)+ } } } From 2fc7810c0b5421499bfc91c5c9bbbfb03d50e151 Mon Sep 17 00:00:00 2001 From: Brooks J Rady Date: Tue, 27 Apr 2021 15:19:55 +0100 Subject: [PATCH 3/8] fix(plugin): mark `Event` as non-exhaustive --- zellij-tile/src/data.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/zellij-tile/src/data.rs b/zellij-tile/src/data.rs index e7ddb319c..f1a77130a 100644 --- a/zellij-tile/src/data.rs +++ b/zellij-tile/src/data.rs @@ -28,6 +28,7 @@ pub enum Key { )] #[strum_discriminants(derive(EnumString, Hash, Serialize, Deserialize))] #[strum_discriminants(name(EventType))] +#[non_exhaustive] pub enum Event { ModeUpdate(ModeInfo), TabUpdate(Vec), From d2aa28ce30cf1f0cb6ea51c46e77a29405e549b4 Mon Sep 17 00:00:00 2001 From: Brooks J Rady Date: Tue, 27 Apr 2021 15:27:16 +0100 Subject: [PATCH 4/8] chore(changelog): new plugin API --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3896b04c2..b6a7277db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) * Doesn't quit anymore on single `q` press while in tab mode (https://github.com/zellij-org/zellij/pull/342) * Completions are not assets anymore, but commands `option --generate-completion [shell]` (https://github.com/zellij-org/zellij/pull/369) * Fixes in the default configuration `default.yaml` file. Adds initial tmux-compat keybindings `tmux.yaml` (https://github.com/zellij-org/zellij/pull/362) +* Added the `get_plugin_ids()` query function to the plugin API (https://github.com/zellij-org/zellij/pull/392) ## [0.5.1] - 2021-04-23 * Change config to flag (https://github.com/zellij-org/zellij/pull/300) From 3d926d6c03f0eaf53d86b48b2ea9d26866f8618e Mon Sep 17 00:00:00 2001 From: Brooks J Rady Date: Tue, 27 Apr 2021 16:05:01 +0100 Subject: [PATCH 5/8] feat(plugin): come back to this commit if you need high-performance timers --- src/common/mod.rs | 14 +++++++++++++- src/common/wasm_vm.rs | 14 ++++++++------ 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/src/common/mod.rs b/src/common/mod.rs index d525a3fe2..2388a162c 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -462,6 +462,18 @@ pub fn start(mut os_input: Box, opts: CliArgs) { let mut plugin_id = 0; let mut plugin_map = HashMap::new(); move || loop { + // BLUEPRINT: + /* next = binheap.get_timer(); // peek + let event = if let Some(time) = next { + // match for the right timeout error here... + if let Some(event) = chan.recv_timeout(time) { + event + } else { + continue + } + } else { + ch.recv().unwrap() + } */ let (event, mut err_ctx) = receive_plugin_instructions .recv() .expect("failed to receive event on channel"); @@ -510,7 +522,7 @@ pub fn start(mut os_input: Box, opts: CliArgs) { let start = instance.exports.get_function("_start").unwrap(); - // This eventually calls the `.init()` method + // This eventually calls the `.load()` method start.call(&[]).unwrap(); plugin_map.insert(plugin_id, (instance, plugin_env)); diff --git a/src/common/wasm_vm.rs b/src/common/wasm_vm.rs index e8990e863..6d31a6bbe 100644 --- a/src/common/wasm_vm.rs +++ b/src/common/wasm_vm.rs @@ -1,10 +1,5 @@ use serde::Serialize; -use std::{ - collections::HashSet, - path::PathBuf, - process, - sync::{mpsc::Sender, Arc, Mutex}, -}; +use std::{collections::HashSet, path::PathBuf, process, sync::{mpsc::Sender, Arc, Mutex}, time::{Duration, Instant}}; use wasmer::{imports, Function, ImportObject, Store, WasmerEnv}; use wasmer_wasi::WasiEnv; use zellij_tile::data::{Event, EventType, PluginIds}; @@ -30,6 +25,13 @@ pub struct PluginEnv { pub send_pty_instructions: SenderWithContext, // FIXME: This should be a big bundle of all of the channels pub wasi_env: WasiEnv, pub subscriptions: Arc>>, + // FIXME: Add timers struct here! BinaryHeap? +} + +struct CallbackTimer { + wake_time: Instant, + plugin_id: u32, + recurring: bool, } // Plugin API --------------------------------------------------------------------------------------------------------- From e163bd56e7e0965ea1b96ca90851f118b54c377c Mon Sep 17 00:00:00 2001 From: Brooks J Rady Date: Tue, 27 Apr 2021 17:13:25 +0100 Subject: [PATCH 6/8] feat(plugin): simple timers implemented via the `set_timeout()` call --- src/common/mod.rs | 20 +++--------- src/common/wasm_vm.rs | 68 +++++++++++++++++++++++++++++++---------- zellij-tile/src/data.rs | 5 ++- zellij-tile/src/shim.rs | 18 ++++++++--- 4 files changed, 73 insertions(+), 38 deletions(-) diff --git a/src/common/mod.rs b/src/common/mod.rs index 2388a162c..27e94f5c8 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -40,7 +40,7 @@ use pty_bus::{PtyBus, PtyInstruction}; use screen::{Screen, ScreenInstruction}; use serde::{Deserialize, Serialize}; use utils::consts::ZELLIJ_IPC_PIPE; -use wasm_vm::{wasi_stdout, wasi_write_json, zellij_exports, PluginEnv, PluginInstruction}; +use wasm_vm::{wasi_read_string, wasi_write_object, zellij_exports, PluginEnv, PluginInstruction}; use wasmer::{ChainableNamedResolver, Instance, Module, Store, Value}; use wasmer_wasi::{Pipe, WasiState}; use zellij_tile::data::{EventType, ModeInfo}; @@ -457,23 +457,12 @@ pub fn start(mut os_input: Box, opts: CliArgs) { let send_pty_instructions = send_pty_instructions.clone(); let send_screen_instructions = send_screen_instructions.clone(); let send_app_instructions = send_app_instructions.clone(); + let send_plugin_instructions = send_plugin_instructions.clone(); let store = Store::default(); let mut plugin_id = 0; let mut plugin_map = HashMap::new(); move || loop { - // BLUEPRINT: - /* next = binheap.get_timer(); // peek - let event = if let Some(time) = next { - // match for the right timeout error here... - if let Some(event) = chan.recv_timeout(time) { - event - } else { - continue - } - } else { - ch.recv().unwrap() - } */ let (event, mut err_ctx) = receive_plugin_instructions .recv() .expect("failed to receive event on channel"); @@ -513,6 +502,7 @@ pub fn start(mut os_input: Box, opts: CliArgs) { send_pty_instructions: send_pty_instructions.clone(), send_screen_instructions: send_screen_instructions.clone(), send_app_instructions: send_app_instructions.clone(), + send_plugin_instructions: send_plugin_instructions.clone(), wasi_env, subscriptions: Arc::new(Mutex::new(HashSet::new())), }; @@ -536,7 +526,7 @@ pub fn start(mut os_input: Box, opts: CliArgs) { 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_json(&plugin_env.wasi_env, &event); + wasi_write_object(&plugin_env.wasi_env, &event); update.call(&[]).unwrap(); } } @@ -551,7 +541,7 @@ pub fn start(mut os_input: Box, opts: CliArgs) { .call(&[Value::I32(rows as i32), Value::I32(cols as i32)]) .unwrap(); - buf_tx.send(wasi_stdout(&plugin_env.wasi_env)).unwrap(); + buf_tx.send(wasi_read_string(&plugin_env.wasi_env)).unwrap(); } 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 6d31a6bbe..bd98a85b8 100644 --- a/src/common/wasm_vm.rs +++ b/src/common/wasm_vm.rs @@ -1,5 +1,12 @@ -use serde::Serialize; -use std::{collections::HashSet, path::PathBuf, process, sync::{mpsc::Sender, Arc, Mutex}, time::{Duration, Instant}}; +use serde::{de::DeserializeOwned, Serialize}; +use std::{ + collections::HashSet, + path::PathBuf, + process, + sync::{mpsc::Sender, Arc, Mutex}, + thread, + time::{Duration, Instant}, +}; use wasmer::{imports, Function, ImportObject, Store, WasmerEnv}; use wasmer_wasi::WasiEnv; use zellij_tile::data::{Event, EventType, PluginIds}; @@ -20,18 +27,13 @@ pub enum PluginInstruction { #[derive(WasmerEnv, Clone)] pub struct PluginEnv { pub plugin_id: u32, + // FIXME: This should be a big bundle of all of the channels pub send_screen_instructions: SenderWithContext, pub send_app_instructions: SenderWithContext, - pub send_pty_instructions: SenderWithContext, // FIXME: This should be a big bundle of all of the channels + pub send_pty_instructions: SenderWithContext, + pub send_plugin_instructions: SenderWithContext, pub wasi_env: WasiEnv, pub subscriptions: Arc>>, - // FIXME: Add timers struct here! BinaryHeap? -} - -struct CallbackTimer { - wake_time: Instant, - plugin_id: u32, - recurring: bool, } // Plugin API --------------------------------------------------------------------------------------------------------- @@ -56,18 +58,19 @@ pub fn zellij_exports(store: &Store, plugin_env: &PluginEnv) -> ImportObject { host_set_selectable, host_get_plugin_ids, host_open_file, + host_set_timeout, } } fn host_subscribe(plugin_env: &PluginEnv) { let mut subscriptions = plugin_env.subscriptions.lock().unwrap(); - let new: HashSet = serde_json::from_str(&wasi_stdout(&plugin_env.wasi_env)).unwrap(); + let new: HashSet = wasi_read_object(&plugin_env.wasi_env); subscriptions.extend(new); } fn host_unsubscribe(plugin_env: &PluginEnv) { let mut subscriptions = plugin_env.subscriptions.lock().unwrap(); - let old: HashSet = serde_json::from_str(&wasi_stdout(&plugin_env.wasi_env)).unwrap(); + let old: HashSet = wasi_read_object(&plugin_env.wasi_env); subscriptions.retain(|k| !old.contains(k)); } @@ -109,21 +112,49 @@ fn host_get_plugin_ids(plugin_env: &PluginEnv) { plugin_id: plugin_env.plugin_id, zellij_pid: process::id(), }; - wasi_write_json(&plugin_env.wasi_env, &ids); + wasi_write_object(&plugin_env.wasi_env, &ids); } fn host_open_file(plugin_env: &PluginEnv) { - let path = PathBuf::from(wasi_stdout(&plugin_env.wasi_env).lines().next().unwrap()); + let path: PathBuf = wasi_read_object(&plugin_env.wasi_env); plugin_env .send_pty_instructions .send(PtyInstruction::SpawnTerminal(Some(path))) .unwrap(); } +fn host_set_timeout(plugin_env: &PluginEnv, secs: f64) { + // There is a fancy, high-performance way to do this with zero additional threads: + // If the plugin thread keeps a BinaryHeap of timer structs, it can manage multiple and easily `.peek()` at the + // next time to trigger in O(1) time. Once the wake-up time is known, the `wasm` thread can use `recv_timeout()` + // to wait for an event with the timeout set to be the time of the next wake up. If events come in in the meantime, + // they are handled, but if the timeout triggers, we replace the event from `recv()` with an + // `Update(pid, TimerEvent)` and pop the timer from the Heap (or reschedule it). No additional threads for as many + // timers as we'd like. + // + // But that's a lot of code, and this is a few lines: + let send_plugin_instructions = plugin_env.send_plugin_instructions.clone(); + let update_target = Some(plugin_env.plugin_id); + thread::spawn(move || { + let start_time = Instant::now(); + thread::sleep(Duration::from_secs_f64(secs)); + // FIXME: The way that elapsed time is being calculated here is not exact; it doesn't take into account the + // time it takes an event to actually reach the plugin after it's sent to the `wasm` thread. + let elapsed_time = Instant::now().duration_since(start_time).as_secs_f64(); + + send_plugin_instructions + .send(PluginInstruction::Update( + update_target, + Event::Timer(elapsed_time), + )) + .unwrap(); + }); +} + // Helper Functions --------------------------------------------------------------------------------------------------- // FIXME: Unwrap city -pub fn wasi_stdout(wasi_env: &WasiEnv) -> String { +pub fn wasi_read_string(wasi_env: &WasiEnv) -> String { let mut state = wasi_env.state(); let wasi_file = state.fs.stdout_mut().unwrap().as_mut().unwrap(); let mut buf = String::new(); @@ -137,6 +168,11 @@ pub fn wasi_write_string(wasi_env: &WasiEnv, buf: &str) { writeln!(wasi_file, "{}\r", buf).unwrap(); } -pub fn wasi_write_json(wasi_env: &WasiEnv, object: &impl Serialize) { +pub fn wasi_write_object(wasi_env: &WasiEnv, object: &impl Serialize) { wasi_write_string(wasi_env, &serde_json::to_string(&object).unwrap()); } + +pub fn wasi_read_object(wasi_env: &WasiEnv) -> T { + let json = wasi_read_string(wasi_env); + serde_json::from_str(&json).unwrap() +} diff --git a/zellij-tile/src/data.rs b/zellij-tile/src/data.rs index f1a77130a..fec37b173 100644 --- a/zellij-tile/src/data.rs +++ b/zellij-tile/src/data.rs @@ -23,9 +23,7 @@ pub enum Key { Esc, } -#[derive( - Debug, Clone, PartialEq, Eq, Hash, EnumDiscriminants, ToString, Serialize, Deserialize, -)] +#[derive(Debug, Clone, PartialEq, EnumDiscriminants, ToString, Serialize, Deserialize)] #[strum_discriminants(derive(EnumString, Hash, Serialize, Deserialize))] #[strum_discriminants(name(EventType))] #[non_exhaustive] @@ -33,6 +31,7 @@ pub enum Event { ModeUpdate(ModeInfo), TabUpdate(Vec), KeyPress(Key), + Timer(f64), } /// Describes the different input modes, which change the way that keystrokes will be interpreted. diff --git a/zellij-tile/src/shim.rs b/zellij-tile/src/shim.rs index ca15919fe..856dcc54d 100644 --- a/zellij-tile/src/shim.rs +++ b/zellij-tile/src/shim.rs @@ -1,4 +1,4 @@ -use serde::de::DeserializeOwned; +use serde::{de::DeserializeOwned, Serialize}; use std::{io, path::Path}; use crate::data::*; @@ -6,12 +6,12 @@ use crate::data::*; // Subscription Handling pub fn subscribe(event_types: &[EventType]) { - println!("{}", serde_json::to_string(event_types).unwrap()); + object_to_stdout(&event_types); unsafe { host_subscribe() }; } pub fn unsubscribe(event_types: &[EventType]) { - println!("{}", serde_json::to_string(event_types).unwrap()); + object_to_stdout(&event_types); unsafe { host_unsubscribe() }; } @@ -38,10 +38,14 @@ pub fn get_plugin_ids() -> PluginIds { // Host Functions pub fn open_file(path: &Path) { - println!("{}", path.to_string_lossy()); + object_to_stdout(&path); unsafe { host_open_file() }; } +pub fn set_timeout(secs: f64) { + unsafe { host_set_timeout(secs) }; +} + // Internal Functions #[doc(hidden)] @@ -51,6 +55,11 @@ pub fn object_from_stdin() -> T { serde_json::from_str(&json).unwrap() } +#[doc(hidden)] +pub fn object_to_stdout(object: &impl Serialize) { + println!("{}", serde_json::to_string(object).unwrap()); +} + #[link(wasm_import_module = "zellij")] extern "C" { fn host_subscribe(); @@ -60,4 +69,5 @@ extern "C" { fn host_set_invisible_borders(invisible_borders: i32); fn host_get_plugin_ids(); fn host_open_file(); + fn host_set_timeout(secs: f64); } From 6ca52d1da3b065e0619655c398f1e63231d55766 Mon Sep 17 00:00:00 2001 From: Brooks J Rady Date: Tue, 27 Apr 2021 17:23:15 +0100 Subject: [PATCH 7/8] chore(changelog): implemented simple plugin timers via the `set_timeout()` call --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b6a7277db..12335bb68 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) * Completions are not assets anymore, but commands `option --generate-completion [shell]` (https://github.com/zellij-org/zellij/pull/369) * Fixes in the default configuration `default.yaml` file. Adds initial tmux-compat keybindings `tmux.yaml` (https://github.com/zellij-org/zellij/pull/362) * Added the `get_plugin_ids()` query function to the plugin API (https://github.com/zellij-org/zellij/pull/392) +* Implemented simple plugin timers via the `set_timeout()` call (https://github.com/zellij-org/zellij/pull/394) ## [0.5.1] - 2021-04-23 * Change config to flag (https://github.com/zellij-org/zellij/pull/300) From fd56d2a1a7305de3c34dc7ffa88f03dc35c7f57b Mon Sep 17 00:00:00 2001 From: Brooks J Rady Date: Tue, 27 Apr 2021 18:52:05 +0100 Subject: [PATCH 8/8] feat(ci): add M1 macOS builds --- .github/workflows/release.yml | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 51d75d398..4744d9f21 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -18,6 +18,7 @@ jobs: - linux musl x64 - linux musl aarch64 - macos x64 + - macos aarch64 include: - build: linux musl x64 os: ubuntu-latest @@ -30,7 +31,11 @@ jobs: - build: macos x64 os: macos-latest rust: beta - target: x86_64-apple-darwin + target: x86_64-apple-darwin + - build: macos aarch64 + os: macos-latest + rust: beta + target: aarch64-apple-darwin steps: - name: Set release tag run: | @@ -64,6 +69,14 @@ jobs: - name: Install wasm-opt run: brew install binaryen + # Workaround for + - name: Switch Xcode SDK + if: runner.os == 'macos' + run: | + cat <> "$GITHUB_ENV" + SDKROOT=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk + EOF + - name: Build release binary run: cargo make ci-build-release ${{ matrix.target }}