aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorBrooks J Rady <b.j.rady@gmail.com>2020-12-29 16:28:34 +0000
committerBrooks J Rady <b.j.rady@gmail.com>2020-12-29 16:28:34 +0000
commitb0c7259266f21652944cf1c74eb42682d9c28fa1 (patch)
tree1f58fef33d79652ead2f421d8a0d3e71926de77b /src
parentMove some wasm code to it's own module (diff)
downloadsl0ppij-b0c7259266f21652944cf1c74eb42682d9c28fa1.tar.gz
sl0ppij-b0c7259266f21652944cf1c74eb42682d9c28fa1.tar.xz
sl0ppij-b0c7259266f21652944cf1c74eb42682d9c28fa1.zip
It's now possible to load WASM files from layouts!
Diffstat (limited to 'src')
-rw-r--r--src/errors.rs26
-rw-r--r--src/layout.rs14
-rw-r--r--src/main.rs163
-rw-r--r--src/tests/fixtures/layouts/panes-with-plugins.yaml17
-rw-r--r--src/wasm_vm.rs12
5 files changed, 165 insertions, 67 deletions
diff --git a/src/errors.rs b/src/errors.rs
index f2e68058..e4788e20 100644
--- a/src/errors.rs
+++ b/src/errors.rs
@@ -101,6 +101,8 @@ impl Display for ErrorContext {
pub enum ContextType {
Screen(ScreenContext),
Pty(PtyContext),
+ #[cfg(feature = "wasm-wip")]
+ Plugin(PluginContext),
App(AppContext),
IPCServer,
StdinHandler,
@@ -115,6 +117,8 @@ impl Display for ContextType {
match *self {
ContextType::Screen(c) => write!(f, "{}screen_thread: {}{:?}", purple, green, c),
ContextType::Pty(c) => write!(f, "{}pty_thread: {}{:?}", purple, green, c),
+ #[cfg(feature = "wasm-wip")]
+ ContextType::Plugin(c) => write!(f, "{}plugin_thread: {}{:?}", purple, green, c),
ContextType::App(c) => write!(f, "{}main_thread: {}{:?}", purple, green, c),
ContextType::IPCServer => write!(f, "{}ipc_server: {}AcceptInput", purple, green),
ContextType::StdinHandler => {
@@ -220,6 +224,28 @@ impl From<&PtyInstruction> for PtyContext {
}
}
+// FIXME: This whole pattern *needs* a macro eventually, it's soul-crushing to write
+#[cfg(feature = "wasm-wip")]
+use crate::wasm_vm::PluginInstruction;
+#[cfg(feature = "wasm-wip")]
+#[derive(Debug, Clone, Copy, PartialEq)]
+pub enum PluginContext {
+ Load,
+ Unload,
+ Quit,
+}
+
+#[cfg(feature = "wasm-wip")]
+impl From<&PluginInstruction> for PluginContext {
+ fn from(plugin_instruction: &PluginInstruction) -> Self {
+ match *plugin_instruction {
+ PluginInstruction::Load(_) => PluginContext::Load,
+ PluginInstruction::Unload(_) => PluginContext::Unload,
+ PluginInstruction::Quit => PluginContext::Quit,
+ }
+ }
+}
+
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum AppContext {
Exit,
diff --git a/src/layout.rs b/src/layout.rs
index 6f110ee1..1e876532 100644
--- a/src/layout.rs
+++ b/src/layout.rs
@@ -140,6 +140,8 @@ pub struct Layout {
pub parts: Vec<Layout>,
#[serde(skip_serializing_if = "Option::is_none")]
pub split_size: Option<SplitSize>,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub plugin: Option<PathBuf>,
}
impl Layout {
@@ -171,6 +173,18 @@ impl Layout {
}
total_panes
}
+
+ // FIXME: I probably shouldn't exist, much less with PathBuf (use &Path)
+ #[cfg(feature = "wasm-wip")]
+ pub fn list_plugins(&self) -> Vec<&PathBuf> {
+ dbg!(&self);
+ let mut plugins: Vec<_> = self.parts.iter().flat_map(Layout::list_plugins).collect();
+ if let Some(path) = &self.plugin {
+ plugins.push(path);
+ }
+ plugins
+ }
+
pub fn position_panes_in_space(&self, space: &PositionAndSize) -> Vec<PositionAndSize> {
split_space(space, &self)
}
diff --git a/src/main.rs b/src/main.rs
index 7b9c67f8..526a67d2 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -159,6 +159,18 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: Opt) {
) = channel();
let mut send_pty_instructions =
SenderWithContext::new(err_ctx, SenderType::Sender(send_pty_instructions));
+
+ #[cfg(feature = "wasm-wip")]
+ use crate::wasm_vm::PluginInstruction;
+ #[cfg(feature = "wasm-wip")]
+ let (send_plugin_instructions, receive_plugin_instructions): (
+ Sender<(PluginInstruction, ErrorContext)>,
+ Receiver<(PluginInstruction, ErrorContext)>,
+ ) = channel();
+ #[cfg(feature = "wasm-wip")]
+ let send_plugin_instructions =
+ SenderWithContext::new(err_ctx, SenderType::Sender(send_plugin_instructions));
+
let (send_app_instructions, receive_app_instructions): (
SyncSender<(AppInstruction, ErrorContext)>,
Receiver<(AppInstruction, ErrorContext)>,
@@ -195,8 +207,17 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: Opt) {
.name("pty".to_string())
.spawn({
let mut command_is_executing = command_is_executing.clone();
+ #[cfg(feature = "wasm-wip")]
+ let send_plugin_instructions = send_plugin_instructions.clone();
move || {
if let Some(layout) = maybe_layout {
+ #[cfg(feature = "wasm-wip")]
+ for plugin_path in layout.list_plugins() {
+ dbg!(send_plugin_instructions
+ .send(PluginInstruction::Load(plugin_path.clone())))
+ .unwrap();
+ }
+
pty_bus.spawn_terminals_for_layout(layout);
} else {
let pid = pty_bus.spawn_terminal(None);
@@ -387,83 +408,89 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: Opt) {
thread::Builder::new()
.name("wasm".to_string())
.spawn(move || {
- // TODO: Clone shared state here
- move || -> Result<(), Box<dyn std::error::Error>> {
- use crate::wasm_vm::{mosaic_imports, wasi_stdout};
- use std::io;
- use wasmer::{ChainableNamedResolver, Instance, Module, Store, Value};
- use wasmer_wasi::{Pipe, WasiState};
+ use crate::errors::PluginContext;
+ use crate::wasm_vm::{mosaic_imports, wasi_stdout};
+ use std::io;
+ use wasmer::{ChainableNamedResolver, Instance, Module, Store, Value};
+ use wasmer_wasi::{Pipe, WasiState};
- let store = Store::default();
+ let store = Store::default();
- println!("Compiling module...");
- // FIXME: Cache this compiled module on disk. I could use `(de)serialize_to_file()` for that
- let module = if let Ok(m) = Module::from_file(&store, "strider.wasm") {
- m
- } else {
- return Ok(()); // Just abort this thread quietly if the WASM isn't found
- };
+ loop {
+ let (event, mut err_ctx) = receive_plugin_instructions
+ .recv()
+ .expect("failed to receive event on channel");
+ err_ctx.add_call(ContextType::Plugin(PluginContext::from(&event)));
+ // FIXME: Clueless on how many of these lines I need...
+ // screen.send_app_instructions.update(err_ctx);
+ // screen.send_pty_instructions.update(err_ctx);
+ match event {
+ PluginInstruction::Load(path) => {
+ // FIXME: Cache this compiled module on disk. I could use `(de)serialize_to_file()` for that
+ let module = Module::from_file(&store, path).unwrap();
- let output = Pipe::new();
- let input = Pipe::new();
- let mut wasi_env = WasiState::new("mosaic")
- .env("CLICOLOR_FORCE", "1")
- .preopen(|p| {
- p.directory(".") // FIXME: Change this to a more meaningful dir
- .alias(".")
- .read(true)
- .write(true)
- .create(true)
- })?
- .stdin(Box::new(input))
- .stdout(Box::new(output))
- .finalize()?;
+ let output = Pipe::new();
+ let input = Pipe::new();
+ let mut wasi_env = WasiState::new("mosaic")
+ .env("CLICOLOR_FORCE", "1")
+ .preopen(|p| {
+ p.directory(".") // FIXME: Change this to a more meaningful dir
+ .alias(".")
+ .read(true)
+ .write(true)
+ .create(true)
+ }).unwrap()
+ .stdin(Box::new(input))
+ .stdout(Box::new(output))
+ .finalize().unwrap();
- let wasi = wasi_env.import_object(&module)?;
- let mosaic = mosaic_imports(&store, &wasi_env);
- let instance = Instance::new(&module, &mosaic.chain_back(wasi))?;
+ let wasi = wasi_env.import_object(&module).unwrap();
+ let mosaic = mosaic_imports(&store, &wasi_env);
+ let instance = Instance::new(&module, &mosaic.chain_back(wasi)).unwrap();
- let start = instance.exports.get_function("_start")?;
- let handle_key = instance.exports.get_function("handle_key")?;
- let draw = instance.exports.get_function("draw")?;
+ let start = instance.exports.get_function("_start").unwrap();
+ let handle_key = instance.exports.get_function("handle_key").unwrap();
+ let draw = instance.exports.get_function("draw").unwrap();
- // This eventually calls the `.init()` method
- start.call(&[])?;
+ // This eventually calls the `.init()` method
+ start.call(&[]).unwrap();
- #[warn(clippy::never_loop)]
- loop {
- let (cols, rows) = (80, 24); //terminal::size()?;
- draw.call(&[Value::I32(rows), Value::I32(cols)])?;
+ #[warn(clippy::never_loop)]
+ loop {
+ let (cols, rows) = (80, 24); //terminal::size()?;
+ draw.call(&[Value::I32(rows), Value::I32(cols)]).unwrap();
- // Needed because raw mode doesn't implicitly return to the start of the line
- write!(
- io::stdout(),
- "{}\n\r",
- wasi_stdout(&wasi_env)
- .lines()
- .collect::<Vec<_>>()
- .join("\n\r")
- )?;
+ // Needed because raw mode doesn't implicitly return to the start of the line
+ write!(
+ io::stdout(),
+ "{}\n\r",
+ wasi_stdout(&wasi_env)
+ .lines()
+ .collect::<Vec<_>>()
+ .join("\n\r")
+ ).unwrap();
- /* match event::read().unwrap() {
- Event::Key(KeyEvent {
- code: KeyCode::Char('q'),
- ..
- }) => break,
- Event::Key(e) => {
- wasi_write_string(&wasi_env, serde_json::to_string(&e).unwrap());
- handle_key.call(&[])?;
+ /* match event::read().unwrap() {
+ Event::Key(KeyEvent {
+ code: KeyCode::Char('q'),
+ ..
+ }) => break,
+ Event::Key(e) => {
+ wasi_write_string(&wasi_env, serde_json::to_string(&e).unwrap());
+ handle_key.call(&[])?;
+ }
+ _ => (),
+ } */
+ break;
}
- _ => (),
- } */
- break;
+ debug_log_to_file("WASM module loaded and exited cleanly :)".to_string()).unwrap();
+ }
+ PluginInstruction::Quit => break,
+ i => panic!("Yo, dawg, nice job calling the wasm thread!\n {:?} is defo not implemented yet...", i),
}
- debug_log_to_file("WASM module loaded and exited cleanly :)".to_string())?;
- Ok(())
- }()
- .unwrap()
- })
- .unwrap(),
+ }
+ }
+ ).unwrap(),
);
// TODO: currently we don't push this into active_threads
@@ -557,11 +584,15 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: Opt) {
AppInstruction::Exit => {
let _ = send_screen_instructions.send(ScreenInstruction::Quit);
let _ = send_pty_instructions.send(PtyInstruction::Quit);
+ #[cfg(feature = "wasm-wip")]
+ let _ = send_plugin_instructions.send(PluginInstruction::Quit);
break;
}
AppInstruction::Error(backtrace) => {
let _ = send_screen_instructions.send(ScreenInstruction::Quit);
let _ = send_pty_instructions.send(PtyInstruction::Quit);
+ #[cfg(feature = "wasm-wip")]
+ let _ = send_plugin_instructions.send(PluginInstruction::Quit);
os_input.unset_raw_mode(0);
let goto_start_of_last_line = format!("\u{1b}[{};{}H", full_screen_ws.rows, 1);
let error = format!("{}\n{}", goto_start_of_last_line, backtrace);
diff --git a/src/tests/fixtures/layouts/panes-with-plugins.yaml b/src/tests/fixtures/layouts/panes-with-plugins.yaml
new file mode 100644
index 00000000..09913bb4
--- /dev/null
+++ b/src/tests/fixtures/layouts/panes-with-plugins.yaml
@@ -0,0 +1,17 @@
+---
+direction: Horizontal
+parts:
+ - direction: Vertical
+ parts:
+ - direction: Horizontal
+ split_size:
+ Percent: 20
+ plugin: strider.wasm
+ - direction: Horizontal
+ split_size:
+ Percent: 80
+ split_size:
+ Percent: 80
+ - direction: Vertical
+ split_size:
+ Percent: 20
diff --git a/src/wasm_vm.rs b/src/wasm_vm.rs
index 334ad0b1..f07d9e48 100644
--- a/src/wasm_vm.rs
+++ b/src/wasm_vm.rs
@@ -1,7 +1,17 @@
-use std::process::{Command, Stdio};
+use std::{
+ path::PathBuf,
+ process::{Command, Stdio},
+};
use wasmer::{imports, Function, ImportObject, Store};
use wasmer_wasi::WasiEnv;
+#[derive(Clone, Debug)]
+pub enum PluginInstruction {
+ Load(PathBuf),
+ Unload(u32),
+ Quit,
+}
+
// Plugin API -----------------------------------------------------------------
pub fn mosaic_imports(store: &Store, wasi_env: &WasiEnv) -> ImportObject {