Merge pull request #126 from mosaic-org/wasm-launchable
Load WASM from Layouts (Includes PR #125)
This commit is contained in:
commit
c7db38d0c5
5 changed files with 165 additions and 68 deletions
|
|
@ -101,6 +101,8 @@ impl Display for ErrorContext {
|
||||||
pub enum ContextType {
|
pub enum ContextType {
|
||||||
Screen(ScreenContext),
|
Screen(ScreenContext),
|
||||||
Pty(PtyContext),
|
Pty(PtyContext),
|
||||||
|
#[cfg(feature = "wasm-wip")]
|
||||||
|
Plugin(PluginContext),
|
||||||
App(AppContext),
|
App(AppContext),
|
||||||
IPCServer,
|
IPCServer,
|
||||||
StdinHandler,
|
StdinHandler,
|
||||||
|
|
@ -115,6 +117,8 @@ impl Display for ContextType {
|
||||||
match *self {
|
match *self {
|
||||||
ContextType::Screen(c) => write!(f, "{}screen_thread: {}{:?}", purple, green, c),
|
ContextType::Screen(c) => write!(f, "{}screen_thread: {}{:?}", purple, green, c),
|
||||||
ContextType::Pty(c) => write!(f, "{}pty_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::App(c) => write!(f, "{}main_thread: {}{:?}", purple, green, c),
|
||||||
ContextType::IPCServer => write!(f, "{}ipc_server: {}AcceptInput", purple, green),
|
ContextType::IPCServer => write!(f, "{}ipc_server: {}AcceptInput", purple, green),
|
||||||
ContextType::StdinHandler => {
|
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)]
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
pub enum AppContext {
|
pub enum AppContext {
|
||||||
Exit,
|
Exit,
|
||||||
|
|
|
||||||
|
|
@ -140,6 +140,8 @@ pub struct Layout {
|
||||||
pub parts: Vec<Layout>,
|
pub parts: Vec<Layout>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub split_size: Option<SplitSize>,
|
pub split_size: Option<SplitSize>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub plugin: Option<PathBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Layout {
|
impl Layout {
|
||||||
|
|
@ -171,6 +173,18 @@ impl Layout {
|
||||||
}
|
}
|
||||||
total_panes
|
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> {
|
pub fn position_panes_in_space(&self, space: &PositionAndSize) -> Vec<PositionAndSize> {
|
||||||
split_space(space, &self)
|
split_space(space, &self)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
164
src/main.rs
164
src/main.rs
|
|
@ -159,6 +159,18 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: Opt) {
|
||||||
) = channel();
|
) = channel();
|
||||||
let mut send_pty_instructions =
|
let mut send_pty_instructions =
|
||||||
SenderWithContext::new(err_ctx, SenderType::Sender(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): (
|
let (send_app_instructions, receive_app_instructions): (
|
||||||
SyncSender<(AppInstruction, ErrorContext)>,
|
SyncSender<(AppInstruction, ErrorContext)>,
|
||||||
Receiver<(AppInstruction, ErrorContext)>,
|
Receiver<(AppInstruction, ErrorContext)>,
|
||||||
|
|
@ -188,9 +200,17 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: Opt) {
|
||||||
.name("pty".to_string())
|
.name("pty".to_string())
|
||||||
.spawn({
|
.spawn({
|
||||||
let mut command_is_executing = command_is_executing.clone();
|
let mut command_is_executing = command_is_executing.clone();
|
||||||
|
#[cfg(feature = "wasm-wip")]
|
||||||
|
let send_plugin_instructions = send_plugin_instructions.clone();
|
||||||
move || {
|
move || {
|
||||||
if let Some(layout) = maybe_layout {
|
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);
|
pty_bus.spawn_terminals_for_layout(layout);
|
||||||
} else {
|
} else {
|
||||||
let pid = pty_bus.spawn_terminal(None);
|
let pid = pty_bus.spawn_terminal(None);
|
||||||
|
|
@ -396,83 +416,89 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: Opt) {
|
||||||
thread::Builder::new()
|
thread::Builder::new()
|
||||||
.name("wasm".to_string())
|
.name("wasm".to_string())
|
||||||
.spawn(move || {
|
.spawn(move || {
|
||||||
// TODO: Clone shared state here
|
use crate::errors::PluginContext;
|
||||||
move || -> Result<(), Box<dyn std::error::Error>> {
|
use crate::wasm_vm::{mosaic_imports, wasi_stdout};
|
||||||
use crate::wasm_vm::{mosaic_imports, wasi_stdout};
|
use std::io;
|
||||||
use std::io;
|
use wasmer::{ChainableNamedResolver, Instance, Module, Store, Value};
|
||||||
use wasmer::{ChainableNamedResolver, Instance, Module, Store, Value};
|
use wasmer_wasi::{Pipe, WasiState};
|
||||||
use wasmer_wasi::{Pipe, WasiState};
|
|
||||||
|
|
||||||
let store = Store::default();
|
let store = Store::default();
|
||||||
|
|
||||||
println!("Compiling module...");
|
loop {
|
||||||
// FIXME: Cache this compiled module on disk. I could use `(de)serialize_to_file()` for that
|
let (event, mut err_ctx) = receive_plugin_instructions
|
||||||
let module = if let Ok(m) = Module::from_file(&store, "strider.wasm") {
|
.recv()
|
||||||
m
|
.expect("failed to receive event on channel");
|
||||||
} else {
|
err_ctx.add_call(ContextType::Plugin(PluginContext::from(&event)));
|
||||||
return Ok(()); // Just abort this thread quietly if the WASM isn't found
|
// 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 output = Pipe::new();
|
||||||
let input = Pipe::new();
|
let input = Pipe::new();
|
||||||
let mut wasi_env = WasiState::new("mosaic")
|
let mut wasi_env = WasiState::new("mosaic")
|
||||||
.env("CLICOLOR_FORCE", "1")
|
.env("CLICOLOR_FORCE", "1")
|
||||||
.preopen(|p| {
|
.preopen(|p| {
|
||||||
p.directory(".") // FIXME: Change this to a more meaningful dir
|
p.directory(".") // FIXME: Change this to a more meaningful dir
|
||||||
.alias(".")
|
.alias(".")
|
||||||
.read(true)
|
.read(true)
|
||||||
.write(true)
|
.write(true)
|
||||||
.create(true)
|
.create(true)
|
||||||
})?
|
}).unwrap()
|
||||||
.stdin(Box::new(input))
|
.stdin(Box::new(input))
|
||||||
.stdout(Box::new(output))
|
.stdout(Box::new(output))
|
||||||
.finalize()?;
|
.finalize().unwrap();
|
||||||
|
|
||||||
let wasi = wasi_env.import_object(&module)?;
|
let wasi = wasi_env.import_object(&module).unwrap();
|
||||||
let mosaic = mosaic_imports(&store, &wasi_env);
|
let mosaic = mosaic_imports(&store, &wasi_env);
|
||||||
let instance = Instance::new(&module, &mosaic.chain_back(wasi))?;
|
let instance = Instance::new(&module, &mosaic.chain_back(wasi)).unwrap();
|
||||||
|
|
||||||
let start = instance.exports.get_function("_start")?;
|
let start = instance.exports.get_function("_start").unwrap();
|
||||||
let handle_key = instance.exports.get_function("handle_key")?;
|
let handle_key = instance.exports.get_function("handle_key").unwrap();
|
||||||
let draw = instance.exports.get_function("draw")?;
|
let draw = instance.exports.get_function("draw").unwrap();
|
||||||
|
|
||||||
// This eventually calls the `.init()` method
|
// This eventually calls the `.init()` method
|
||||||
start.call(&[])?;
|
start.call(&[]).unwrap();
|
||||||
|
|
||||||
#[warn(clippy::never_loop)]
|
#[warn(clippy::never_loop)]
|
||||||
loop {
|
loop {
|
||||||
let (cols, rows) = (80, 24); //terminal::size()?;
|
let (cols, rows) = (80, 24); //terminal::size()?;
|
||||||
draw.call(&[Value::I32(rows), Value::I32(cols)])?;
|
draw.call(&[Value::I32(rows), Value::I32(cols)]).unwrap();
|
||||||
|
|
||||||
// Needed because raw mode doesn't implicitly return to the start of the line
|
// Needed because raw mode doesn't implicitly return to the start of the line
|
||||||
write!(
|
write!(
|
||||||
io::stdout(),
|
io::stdout(),
|
||||||
"{}\n\r",
|
"{}\n\r",
|
||||||
wasi_stdout(&wasi_env)
|
wasi_stdout(&wasi_env)
|
||||||
.lines()
|
.lines()
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join("\n\r")
|
.join("\n\r")
|
||||||
)?;
|
).unwrap();
|
||||||
|
|
||||||
/* match event::read().unwrap() {
|
/* match event::read().unwrap() {
|
||||||
Event::Key(KeyEvent {
|
Event::Key(KeyEvent {
|
||||||
code: KeyCode::Char('q'),
|
code: KeyCode::Char('q'),
|
||||||
..
|
..
|
||||||
}) => break,
|
}) => break,
|
||||||
Event::Key(e) => {
|
Event::Key(e) => {
|
||||||
wasi_write_string(&wasi_env, serde_json::to_string(&e).unwrap());
|
wasi_write_string(&wasi_env, serde_json::to_string(&e).unwrap());
|
||||||
handle_key.call(&[])?;
|
handle_key.call(&[])?;
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
} */
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
_ => (),
|
debug_log_to_file("WASM module loaded and exited cleanly :)".to_string()).unwrap();
|
||||||
} */
|
}
|
||||||
break;
|
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
|
// TODO: currently we don't push this into active_threads
|
||||||
|
|
@ -566,11 +592,15 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: Opt) {
|
||||||
AppInstruction::Exit => {
|
AppInstruction::Exit => {
|
||||||
let _ = send_screen_instructions.send(ScreenInstruction::Quit);
|
let _ = send_screen_instructions.send(ScreenInstruction::Quit);
|
||||||
let _ = send_pty_instructions.send(PtyInstruction::Quit);
|
let _ = send_pty_instructions.send(PtyInstruction::Quit);
|
||||||
|
#[cfg(feature = "wasm-wip")]
|
||||||
|
let _ = send_plugin_instructions.send(PluginInstruction::Quit);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
AppInstruction::Error(backtrace) => {
|
AppInstruction::Error(backtrace) => {
|
||||||
let _ = send_screen_instructions.send(ScreenInstruction::Quit);
|
let _ = send_screen_instructions.send(ScreenInstruction::Quit);
|
||||||
let _ = send_pty_instructions.send(PtyInstruction::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);
|
os_input.unset_raw_mode(0);
|
||||||
let goto_start_of_last_line = format!("\u{1b}[{};{}H", full_screen_ws.rows, 1);
|
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);
|
let error = format!("{}\n{}", goto_start_of_last_line, backtrace);
|
||||||
|
|
|
||||||
17
src/tests/fixtures/layouts/panes-with-plugins.yaml
vendored
Normal file
17
src/tests/fixtures/layouts/panes-with-plugins.yaml
vendored
Normal file
|
|
@ -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
|
||||||
|
|
@ -1,7 +1,17 @@
|
||||||
use std::process::{Command, Stdio};
|
use std::{
|
||||||
|
path::PathBuf,
|
||||||
|
process::{Command, Stdio},
|
||||||
|
};
|
||||||
use wasmer::{imports, Function, ImportObject, Store};
|
use wasmer::{imports, Function, ImportObject, Store};
|
||||||
use wasmer_wasi::WasiEnv;
|
use wasmer_wasi::WasiEnv;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum PluginInstruction {
|
||||||
|
Load(PathBuf),
|
||||||
|
Unload(u32),
|
||||||
|
Quit,
|
||||||
|
}
|
||||||
|
|
||||||
// Plugin API -----------------------------------------------------------------
|
// Plugin API -----------------------------------------------------------------
|
||||||
|
|
||||||
pub fn mosaic_imports(store: &Store, wasi_env: &WasiEnv) -> ImportObject {
|
pub fn mosaic_imports(store: &Store, wasi_env: &WasiEnv) -> ImportObject {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue