mirror of
https://github.com/zellij-org/zellij.git
synced 2024-12-27 03:04:16 +03:00
It's now possible to load WASM files from layouts!
This commit is contained in:
parent
0049b26bf8
commit
b0c7259266
@ -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,
|
||||
|
@ -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)
|
||||
}
|
||||
|
163
src/main.rs
163
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);
|
||||
|
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_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 {
|
||||
|
Loading…
Reference in New Issue
Block a user