fix(main): do not hang on exit (#150)
This commit is contained in:
parent
81af57b15d
commit
decc38232b
2 changed files with 305 additions and 316 deletions
613
src/main.rs
613
src/main.rs
|
|
@ -173,7 +173,6 @@ pub enum AppInstruction {
|
||||||
|
|
||||||
pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
|
pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
|
||||||
let mut app_state = AppState::default();
|
let mut app_state = AppState::default();
|
||||||
let mut active_threads = vec![];
|
|
||||||
|
|
||||||
let command_is_executing = CommandIsExecuting::new();
|
let command_is_executing = CommandIsExecuting::new();
|
||||||
|
|
||||||
|
|
@ -225,345 +224,334 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
active_threads.push(
|
let pty_thread = thread::Builder::new()
|
||||||
thread::Builder::new()
|
.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();
|
send_pty_instructions.send(PtyInstruction::NewTab).unwrap();
|
||||||
send_pty_instructions.send(PtyInstruction::NewTab).unwrap();
|
move || loop {
|
||||||
move || loop {
|
let (event, mut err_ctx) = pty_bus
|
||||||
let (event, mut err_ctx) = pty_bus
|
.receive_pty_instructions
|
||||||
.receive_pty_instructions
|
.recv()
|
||||||
|
.expect("failed to receive event on channel");
|
||||||
|
err_ctx.add_call(ContextType::Pty(PtyContext::from(&event)));
|
||||||
|
pty_bus.send_screen_instructions.update(err_ctx);
|
||||||
|
match event {
|
||||||
|
PtyInstruction::SpawnTerminal(file_to_open) => {
|
||||||
|
let pid = pty_bus.spawn_terminal(file_to_open);
|
||||||
|
pty_bus
|
||||||
|
.send_screen_instructions
|
||||||
|
.send(ScreenInstruction::NewPane(PaneId::Terminal(pid)))
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
PtyInstruction::SpawnTerminalVertically(file_to_open) => {
|
||||||
|
let pid = pty_bus.spawn_terminal(file_to_open);
|
||||||
|
pty_bus
|
||||||
|
.send_screen_instructions
|
||||||
|
.send(ScreenInstruction::VerticalSplit(PaneId::Terminal(pid)))
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
PtyInstruction::SpawnTerminalHorizontally(file_to_open) => {
|
||||||
|
let pid = pty_bus.spawn_terminal(file_to_open);
|
||||||
|
pty_bus
|
||||||
|
.send_screen_instructions
|
||||||
|
.send(ScreenInstruction::HorizontalSplit(PaneId::Terminal(pid)))
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
PtyInstruction::NewTab => {
|
||||||
|
if let Some(layout) = maybe_layout.clone() {
|
||||||
|
pty_bus.spawn_terminals_for_layout(layout);
|
||||||
|
} else {
|
||||||
|
let pid = pty_bus.spawn_terminal(None);
|
||||||
|
pty_bus
|
||||||
|
.send_screen_instructions
|
||||||
|
.send(ScreenInstruction::NewTab(pid))
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PtyInstruction::ClosePane(id) => {
|
||||||
|
pty_bus.close_pane(id);
|
||||||
|
command_is_executing.done_closing_pane();
|
||||||
|
}
|
||||||
|
PtyInstruction::CloseTab(ids) => {
|
||||||
|
pty_bus.close_tab(ids);
|
||||||
|
command_is_executing.done_closing_pane();
|
||||||
|
}
|
||||||
|
PtyInstruction::Quit => {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let screen_thread = thread::Builder::new()
|
||||||
|
.name("screen".to_string())
|
||||||
|
.spawn({
|
||||||
|
let mut command_is_executing = command_is_executing.clone();
|
||||||
|
let os_input = os_input.clone();
|
||||||
|
let send_pty_instructions = send_pty_instructions.clone();
|
||||||
|
let send_plugin_instructions = send_plugin_instructions.clone();
|
||||||
|
let send_app_instructions = send_app_instructions.clone();
|
||||||
|
let max_panes = opts.max_panes;
|
||||||
|
|
||||||
|
move || {
|
||||||
|
let mut screen = Screen::new(
|
||||||
|
receive_screen_instructions,
|
||||||
|
send_pty_instructions,
|
||||||
|
send_plugin_instructions,
|
||||||
|
send_app_instructions,
|
||||||
|
&full_screen_ws,
|
||||||
|
os_input,
|
||||||
|
max_panes,
|
||||||
|
);
|
||||||
|
loop {
|
||||||
|
let (event, mut err_ctx) = screen
|
||||||
|
.receiver
|
||||||
.recv()
|
.recv()
|
||||||
.expect("failed to receive event on channel");
|
.expect("failed to receive event on channel");
|
||||||
err_ctx.add_call(ContextType::Pty(PtyContext::from(&event)));
|
err_ctx.add_call(ContextType::Screen(ScreenContext::from(&event)));
|
||||||
pty_bus.send_screen_instructions.update(err_ctx);
|
screen.send_app_instructions.update(err_ctx);
|
||||||
|
screen.send_pty_instructions.update(err_ctx);
|
||||||
match event {
|
match event {
|
||||||
PtyInstruction::SpawnTerminal(file_to_open) => {
|
ScreenInstruction::Pty(pid, vte_event) => {
|
||||||
let pid = pty_bus.spawn_terminal(file_to_open);
|
screen
|
||||||
pty_bus
|
.get_active_tab_mut()
|
||||||
.send_screen_instructions
|
.unwrap()
|
||||||
.send(ScreenInstruction::NewPane(PaneId::Terminal(pid)))
|
.handle_pty_event(pid, vte_event);
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
PtyInstruction::SpawnTerminalVertically(file_to_open) => {
|
ScreenInstruction::Render => {
|
||||||
let pid = pty_bus.spawn_terminal(file_to_open);
|
screen.render();
|
||||||
pty_bus
|
|
||||||
.send_screen_instructions
|
|
||||||
.send(ScreenInstruction::VerticalSplit(PaneId::Terminal(pid)))
|
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
PtyInstruction::SpawnTerminalHorizontally(file_to_open) => {
|
ScreenInstruction::NewPane(pid) => {
|
||||||
let pid = pty_bus.spawn_terminal(file_to_open);
|
screen.get_active_tab_mut().unwrap().new_pane(pid);
|
||||||
pty_bus
|
command_is_executing.done_opening_new_pane();
|
||||||
.send_screen_instructions
|
|
||||||
.send(ScreenInstruction::HorizontalSplit(PaneId::Terminal(pid)))
|
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
PtyInstruction::NewTab => {
|
ScreenInstruction::HorizontalSplit(pid) => {
|
||||||
if let Some(layout) = maybe_layout.clone() {
|
screen.get_active_tab_mut().unwrap().horizontal_split(pid);
|
||||||
pty_bus.spawn_terminals_for_layout(layout);
|
command_is_executing.done_opening_new_pane();
|
||||||
} else {
|
|
||||||
let pid = pty_bus.spawn_terminal(None);
|
|
||||||
pty_bus
|
|
||||||
.send_screen_instructions
|
|
||||||
.send(ScreenInstruction::NewTab(pid))
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
PtyInstruction::ClosePane(id) => {
|
ScreenInstruction::VerticalSplit(pid) => {
|
||||||
pty_bus.close_pane(id);
|
screen.get_active_tab_mut().unwrap().vertical_split(pid);
|
||||||
command_is_executing.done_closing_pane();
|
command_is_executing.done_opening_new_pane();
|
||||||
}
|
}
|
||||||
PtyInstruction::CloseTab(ids) => {
|
ScreenInstruction::WriteCharacter(bytes) => {
|
||||||
pty_bus.close_tab(ids);
|
screen
|
||||||
command_is_executing.done_closing_pane();
|
.get_active_tab_mut()
|
||||||
|
.unwrap()
|
||||||
|
.write_to_active_terminal(bytes);
|
||||||
}
|
}
|
||||||
PtyInstruction::Quit => {
|
ScreenInstruction::ResizeLeft => {
|
||||||
|
screen.get_active_tab_mut().unwrap().resize_left();
|
||||||
|
}
|
||||||
|
ScreenInstruction::ResizeRight => {
|
||||||
|
screen.get_active_tab_mut().unwrap().resize_right();
|
||||||
|
}
|
||||||
|
ScreenInstruction::ResizeDown => {
|
||||||
|
screen.get_active_tab_mut().unwrap().resize_down();
|
||||||
|
}
|
||||||
|
ScreenInstruction::ResizeUp => {
|
||||||
|
screen.get_active_tab_mut().unwrap().resize_up();
|
||||||
|
}
|
||||||
|
ScreenInstruction::MoveFocus => {
|
||||||
|
screen.get_active_tab_mut().unwrap().move_focus();
|
||||||
|
}
|
||||||
|
ScreenInstruction::MoveFocusLeft => {
|
||||||
|
screen.get_active_tab_mut().unwrap().move_focus_left();
|
||||||
|
}
|
||||||
|
ScreenInstruction::MoveFocusDown => {
|
||||||
|
screen.get_active_tab_mut().unwrap().move_focus_down();
|
||||||
|
}
|
||||||
|
ScreenInstruction::MoveFocusRight => {
|
||||||
|
screen.get_active_tab_mut().unwrap().move_focus_right();
|
||||||
|
}
|
||||||
|
ScreenInstruction::MoveFocusUp => {
|
||||||
|
screen.get_active_tab_mut().unwrap().move_focus_up();
|
||||||
|
}
|
||||||
|
ScreenInstruction::ScrollUp => {
|
||||||
|
screen
|
||||||
|
.get_active_tab_mut()
|
||||||
|
.unwrap()
|
||||||
|
.scroll_active_terminal_up();
|
||||||
|
}
|
||||||
|
ScreenInstruction::ScrollDown => {
|
||||||
|
screen
|
||||||
|
.get_active_tab_mut()
|
||||||
|
.unwrap()
|
||||||
|
.scroll_active_terminal_down();
|
||||||
|
}
|
||||||
|
ScreenInstruction::ClearScroll => {
|
||||||
|
screen
|
||||||
|
.get_active_tab_mut()
|
||||||
|
.unwrap()
|
||||||
|
.clear_active_terminal_scroll();
|
||||||
|
}
|
||||||
|
ScreenInstruction::CloseFocusedPane => {
|
||||||
|
screen.get_active_tab_mut().unwrap().close_focused_pane();
|
||||||
|
screen.render();
|
||||||
|
}
|
||||||
|
ScreenInstruction::SetSelectable(id, selectable) => {
|
||||||
|
screen
|
||||||
|
.get_active_tab_mut()
|
||||||
|
.unwrap()
|
||||||
|
.set_pane_selectable(id, selectable);
|
||||||
|
// FIXME: Is this needed?
|
||||||
|
screen.render();
|
||||||
|
}
|
||||||
|
ScreenInstruction::ClosePane(id) => {
|
||||||
|
screen.get_active_tab_mut().unwrap().close_pane(id);
|
||||||
|
screen.render();
|
||||||
|
}
|
||||||
|
ScreenInstruction::ToggleActiveTerminalFullscreen => {
|
||||||
|
screen
|
||||||
|
.get_active_tab_mut()
|
||||||
|
.unwrap()
|
||||||
|
.toggle_active_pane_fullscreen();
|
||||||
|
}
|
||||||
|
ScreenInstruction::NewTab(pane_id) => {
|
||||||
|
screen.new_tab(pane_id);
|
||||||
|
command_is_executing.done_opening_new_pane();
|
||||||
|
}
|
||||||
|
ScreenInstruction::SwitchTabNext => screen.switch_tab_next(),
|
||||||
|
ScreenInstruction::SwitchTabPrev => screen.switch_tab_prev(),
|
||||||
|
ScreenInstruction::CloseTab => screen.close_tab(),
|
||||||
|
ScreenInstruction::ApplyLayout((layout, new_pane_pids)) => {
|
||||||
|
screen.apply_layout(layout, new_pane_pids);
|
||||||
|
command_is_executing.done_opening_new_pane();
|
||||||
|
}
|
||||||
|
ScreenInstruction::Quit => {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
.unwrap(),
|
})
|
||||||
);
|
.unwrap();
|
||||||
|
|
||||||
active_threads.push(
|
let wasm_thread = thread::Builder::new()
|
||||||
thread::Builder::new()
|
.name("wasm".to_string())
|
||||||
.name("screen".to_string())
|
.spawn({
|
||||||
.spawn({
|
let mut send_pty_instructions = send_pty_instructions.clone();
|
||||||
let mut command_is_executing = command_is_executing.clone();
|
let mut send_screen_instructions = send_screen_instructions.clone();
|
||||||
let os_input = os_input.clone();
|
let mut send_app_instructions = send_app_instructions.clone();
|
||||||
let send_pty_instructions = send_pty_instructions.clone();
|
|
||||||
let send_plugin_instructions = send_plugin_instructions.clone();
|
|
||||||
let send_app_instructions = send_app_instructions.clone();
|
|
||||||
let max_panes = opts.max_panes;
|
|
||||||
|
|
||||||
move || {
|
let store = Store::default();
|
||||||
let mut screen = Screen::new(
|
let mut plugin_id = 0;
|
||||||
receive_screen_instructions,
|
let mut plugin_map = HashMap::new();
|
||||||
send_pty_instructions,
|
|
||||||
send_plugin_instructions,
|
move || loop {
|
||||||
send_app_instructions,
|
let (event, mut err_ctx) = receive_plugin_instructions
|
||||||
&full_screen_ws,
|
.recv()
|
||||||
os_input,
|
.expect("failed to receive event on channel");
|
||||||
max_panes,
|
err_ctx.add_call(ContextType::Plugin(PluginContext::from(&event)));
|
||||||
);
|
send_screen_instructions.update(err_ctx);
|
||||||
loop {
|
send_pty_instructions.update(err_ctx);
|
||||||
let (event, mut err_ctx) = screen
|
send_app_instructions.update(err_ctx);
|
||||||
.receiver
|
match event {
|
||||||
.recv()
|
PluginInstruction::Load(pid_tx, path) => {
|
||||||
.expect("failed to receive event on channel");
|
let project_dirs =
|
||||||
err_ctx.add_call(ContextType::Screen(ScreenContext::from(&event)));
|
ProjectDirs::from("org", "Mosaic Contributors", "Mosaic").unwrap();
|
||||||
screen.send_app_instructions.update(err_ctx);
|
let plugin_dir = project_dirs.data_dir().join("plugins/");
|
||||||
screen.send_pty_instructions.update(err_ctx);
|
let wasm_bytes = fs::read(&path)
|
||||||
match event {
|
.or_else(|_| fs::read(&path.with_extension("wasm")))
|
||||||
ScreenInstruction::Pty(pid, vte_event) => {
|
.or_else(|_| fs::read(&plugin_dir.join(&path).with_extension("wasm")))
|
||||||
screen
|
.unwrap_or_else(|_| panic!("cannot find plugin {}", &path.display()));
|
||||||
.get_active_tab_mut()
|
|
||||||
.unwrap()
|
// FIXME: Cache this compiled module on disk. I could use `(de)serialize_to_file()` for that
|
||||||
.handle_pty_event(pid, vte_event);
|
let module = Module::new(&store, &wasm_bytes).unwrap();
|
||||||
}
|
|
||||||
ScreenInstruction::Render => {
|
let output = Pipe::new();
|
||||||
screen.render();
|
let input = Pipe::new();
|
||||||
}
|
let mut wasi_env = WasiState::new("mosaic")
|
||||||
ScreenInstruction::NewPane(pid) => {
|
.env("CLICOLOR_FORCE", "1")
|
||||||
screen.get_active_tab_mut().unwrap().new_pane(pid);
|
.preopen(|p| {
|
||||||
command_is_executing.done_opening_new_pane();
|
p.directory(".") // FIXME: Change this to a more meaningful dir
|
||||||
}
|
.alias(".")
|
||||||
ScreenInstruction::HorizontalSplit(pid) => {
|
.read(true)
|
||||||
screen.get_active_tab_mut().unwrap().horizontal_split(pid);
|
.write(true)
|
||||||
command_is_executing.done_opening_new_pane();
|
.create(true)
|
||||||
}
|
})
|
||||||
ScreenInstruction::VerticalSplit(pid) => {
|
.unwrap()
|
||||||
screen.get_active_tab_mut().unwrap().vertical_split(pid);
|
.stdin(Box::new(input))
|
||||||
command_is_executing.done_opening_new_pane();
|
.stdout(Box::new(output))
|
||||||
}
|
.finalize()
|
||||||
ScreenInstruction::WriteCharacter(bytes) => {
|
.unwrap();
|
||||||
screen
|
|
||||||
.get_active_tab_mut()
|
let wasi = wasi_env.import_object(&module).unwrap();
|
||||||
.unwrap()
|
|
||||||
.write_to_active_terminal(bytes);
|
let plugin_env = PluginEnv {
|
||||||
}
|
plugin_id,
|
||||||
ScreenInstruction::ResizeLeft => {
|
send_pty_instructions: send_pty_instructions.clone(),
|
||||||
screen.get_active_tab_mut().unwrap().resize_left();
|
send_screen_instructions: send_screen_instructions.clone(),
|
||||||
}
|
send_app_instructions: send_app_instructions.clone(),
|
||||||
ScreenInstruction::ResizeRight => {
|
wasi_env,
|
||||||
screen.get_active_tab_mut().unwrap().resize_right();
|
};
|
||||||
}
|
|
||||||
ScreenInstruction::ResizeDown => {
|
let mosaic = mosaic_imports(&store, &plugin_env);
|
||||||
screen.get_active_tab_mut().unwrap().resize_down();
|
let instance = Instance::new(&module, &mosaic.chain_back(wasi)).unwrap();
|
||||||
}
|
|
||||||
ScreenInstruction::ResizeUp => {
|
let start = instance.exports.get_function("_start").unwrap();
|
||||||
screen.get_active_tab_mut().unwrap().resize_up();
|
|
||||||
}
|
// This eventually calls the `.init()` method
|
||||||
ScreenInstruction::MoveFocus => {
|
start.call(&[]).unwrap();
|
||||||
screen.get_active_tab_mut().unwrap().move_focus();
|
|
||||||
}
|
plugin_map.insert(plugin_id, (instance, plugin_env));
|
||||||
ScreenInstruction::MoveFocusLeft => {
|
pid_tx.send(plugin_id).unwrap();
|
||||||
screen.get_active_tab_mut().unwrap().move_focus_left();
|
plugin_id += 1;
|
||||||
}
|
|
||||||
ScreenInstruction::MoveFocusDown => {
|
|
||||||
screen.get_active_tab_mut().unwrap().move_focus_down();
|
|
||||||
}
|
|
||||||
ScreenInstruction::MoveFocusRight => {
|
|
||||||
screen.get_active_tab_mut().unwrap().move_focus_right();
|
|
||||||
}
|
|
||||||
ScreenInstruction::MoveFocusUp => {
|
|
||||||
screen.get_active_tab_mut().unwrap().move_focus_up();
|
|
||||||
}
|
|
||||||
ScreenInstruction::ScrollUp => {
|
|
||||||
screen
|
|
||||||
.get_active_tab_mut()
|
|
||||||
.unwrap()
|
|
||||||
.scroll_active_terminal_up();
|
|
||||||
}
|
|
||||||
ScreenInstruction::ScrollDown => {
|
|
||||||
screen
|
|
||||||
.get_active_tab_mut()
|
|
||||||
.unwrap()
|
|
||||||
.scroll_active_terminal_down();
|
|
||||||
}
|
|
||||||
ScreenInstruction::ClearScroll => {
|
|
||||||
screen
|
|
||||||
.get_active_tab_mut()
|
|
||||||
.unwrap()
|
|
||||||
.clear_active_terminal_scroll();
|
|
||||||
}
|
|
||||||
ScreenInstruction::CloseFocusedPane => {
|
|
||||||
screen.get_active_tab_mut().unwrap().close_focused_pane();
|
|
||||||
screen.render();
|
|
||||||
}
|
|
||||||
ScreenInstruction::SetSelectable(id, selectable) => {
|
|
||||||
screen
|
|
||||||
.get_active_tab_mut()
|
|
||||||
.unwrap()
|
|
||||||
.set_pane_selectable(id, selectable);
|
|
||||||
// FIXME: Is this needed?
|
|
||||||
screen.render();
|
|
||||||
}
|
|
||||||
ScreenInstruction::ClosePane(id) => {
|
|
||||||
screen.get_active_tab_mut().unwrap().close_pane(id);
|
|
||||||
screen.render();
|
|
||||||
}
|
|
||||||
ScreenInstruction::ToggleActiveTerminalFullscreen => {
|
|
||||||
screen
|
|
||||||
.get_active_tab_mut()
|
|
||||||
.unwrap()
|
|
||||||
.toggle_active_pane_fullscreen();
|
|
||||||
}
|
|
||||||
ScreenInstruction::NewTab(pane_id) => {
|
|
||||||
screen.new_tab(pane_id);
|
|
||||||
command_is_executing.done_opening_new_pane();
|
|
||||||
}
|
|
||||||
ScreenInstruction::SwitchTabNext => screen.switch_tab_next(),
|
|
||||||
ScreenInstruction::SwitchTabPrev => screen.switch_tab_prev(),
|
|
||||||
ScreenInstruction::CloseTab => screen.close_tab(),
|
|
||||||
ScreenInstruction::ApplyLayout((layout, new_pane_pids)) => {
|
|
||||||
screen.apply_layout(layout, new_pane_pids);
|
|
||||||
command_is_executing.done_opening_new_pane();
|
|
||||||
}
|
|
||||||
ScreenInstruction::Quit => {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
PluginInstruction::Draw(buf_tx, pid, rows, cols) => {
|
||||||
})
|
let (instance, plugin_env) = plugin_map.get(&pid).unwrap();
|
||||||
.unwrap(),
|
|
||||||
);
|
|
||||||
|
|
||||||
active_threads.push(
|
let draw = instance.exports.get_function("draw").unwrap();
|
||||||
thread::Builder::new()
|
|
||||||
.name("wasm".to_string())
|
|
||||||
.spawn({
|
|
||||||
let mut send_pty_instructions = send_pty_instructions.clone();
|
|
||||||
let mut send_screen_instructions = send_screen_instructions.clone();
|
|
||||||
let mut send_app_instructions = send_app_instructions.clone();
|
|
||||||
|
|
||||||
let store = Store::default();
|
draw.call(&[Value::I32(rows as i32), Value::I32(cols as i32)])
|
||||||
let mut plugin_id = 0;
|
.unwrap();
|
||||||
let mut plugin_map = HashMap::new();
|
|
||||||
|
|
||||||
move || loop {
|
buf_tx.send(wasi_stdout(&plugin_env.wasi_env)).unwrap();
|
||||||
let (event, mut err_ctx) = receive_plugin_instructions
|
}
|
||||||
.recv()
|
// FIXME: Deduplicate this with the callback below!
|
||||||
.expect("failed to receive event on channel");
|
PluginInstruction::Input(pid, input_bytes) => {
|
||||||
err_ctx.add_call(ContextType::Plugin(PluginContext::from(&event)));
|
let (instance, plugin_env) = plugin_map.get(&pid).unwrap();
|
||||||
send_screen_instructions.update(err_ctx);
|
|
||||||
send_pty_instructions.update(err_ctx);
|
|
||||||
send_app_instructions.update(err_ctx);
|
|
||||||
match event {
|
|
||||||
PluginInstruction::Load(pid_tx, path) => {
|
|
||||||
let project_dirs =
|
|
||||||
ProjectDirs::from("org", "Mosaic Contributors", "Mosaic").unwrap();
|
|
||||||
let plugin_dir = project_dirs.data_dir().join("plugins/");
|
|
||||||
let wasm_bytes = fs::read(&path)
|
|
||||||
.or_else(|_| fs::read(&path.with_extension("wasm")))
|
|
||||||
.or_else(|_| {
|
|
||||||
fs::read(&plugin_dir.join(&path).with_extension("wasm"))
|
|
||||||
})
|
|
||||||
.unwrap_or_else(|_| {
|
|
||||||
panic!("cannot find plugin {}", &path.display())
|
|
||||||
});
|
|
||||||
|
|
||||||
// FIXME: Cache this compiled module on disk. I could use `(de)serialize_to_file()` for that
|
let handle_key = instance.exports.get_function("handle_key").unwrap();
|
||||||
let module = Module::new(&store, &wasm_bytes).unwrap();
|
for key in input_bytes.keys() {
|
||||||
|
if let Ok(key) = key {
|
||||||
let output = Pipe::new();
|
wasi_write_string(
|
||||||
let input = Pipe::new();
|
&plugin_env.wasi_env,
|
||||||
let mut wasi_env = WasiState::new("mosaic")
|
&serde_json::to_string(&key).unwrap(),
|
||||||
.env("CLICOLOR_FORCE", "1")
|
);
|
||||||
.preopen(|p| {
|
handle_key.call(&[]).unwrap();
|
||||||
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).unwrap();
|
|
||||||
|
|
||||||
let plugin_env = PluginEnv {
|
|
||||||
plugin_id,
|
|
||||||
send_pty_instructions: send_pty_instructions.clone(),
|
|
||||||
send_screen_instructions: send_screen_instructions.clone(),
|
|
||||||
send_app_instructions: send_app_instructions.clone(),
|
|
||||||
wasi_env,
|
|
||||||
};
|
|
||||||
|
|
||||||
let mosaic = mosaic_imports(&store, &plugin_env);
|
|
||||||
let instance =
|
|
||||||
Instance::new(&module, &mosaic.chain_back(wasi)).unwrap();
|
|
||||||
|
|
||||||
let start = instance.exports.get_function("_start").unwrap();
|
|
||||||
|
|
||||||
// This eventually calls the `.init()` method
|
|
||||||
start.call(&[]).unwrap();
|
|
||||||
|
|
||||||
plugin_map.insert(plugin_id, (instance, plugin_env));
|
|
||||||
pid_tx.send(plugin_id).unwrap();
|
|
||||||
plugin_id += 1;
|
|
||||||
}
|
}
|
||||||
PluginInstruction::Draw(buf_tx, pid, rows, cols) => {
|
|
||||||
let (instance, plugin_env) = plugin_map.get(&pid).unwrap();
|
|
||||||
|
|
||||||
let draw = instance.exports.get_function("draw").unwrap();
|
drop(send_screen_instructions.send(ScreenInstruction::Render));
|
||||||
|
}
|
||||||
draw.call(&[Value::I32(rows as i32), Value::I32(cols as i32)])
|
PluginInstruction::GlobalInput(input_bytes) => {
|
||||||
.unwrap();
|
// FIXME: Set up an event subscription system, and timed callbacks
|
||||||
|
for (instance, plugin_env) in plugin_map.values() {
|
||||||
buf_tx.send(wasi_stdout(&plugin_env.wasi_env)).unwrap();
|
let handler =
|
||||||
}
|
instance.exports.get_function("handle_global_key").unwrap();
|
||||||
// FIXME: Deduplicate this with the callback below!
|
|
||||||
PluginInstruction::Input(pid, input_bytes) => {
|
|
||||||
let (instance, plugin_env) = plugin_map.get(&pid).unwrap();
|
|
||||||
|
|
||||||
let handle_key = instance.exports.get_function("handle_key").unwrap();
|
|
||||||
for key in input_bytes.keys() {
|
for key in input_bytes.keys() {
|
||||||
if let Ok(key) = key {
|
if let Ok(key) = key {
|
||||||
wasi_write_string(
|
wasi_write_string(
|
||||||
&plugin_env.wasi_env,
|
&plugin_env.wasi_env,
|
||||||
&serde_json::to_string(&key).unwrap(),
|
&serde_json::to_string(&key).unwrap(),
|
||||||
);
|
);
|
||||||
handle_key.call(&[]).unwrap();
|
handler.call(&[]).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
drop(send_screen_instructions.send(ScreenInstruction::Render));
|
|
||||||
}
|
}
|
||||||
PluginInstruction::GlobalInput(input_bytes) => {
|
|
||||||
// FIXME: Set up an event subscription system, and timed callbacks
|
|
||||||
for (instance, plugin_env) in plugin_map.values() {
|
|
||||||
let handler =
|
|
||||||
instance.exports.get_function("handle_global_key").unwrap();
|
|
||||||
for key in input_bytes.keys() {
|
|
||||||
if let Ok(key) = key {
|
|
||||||
wasi_write_string(
|
|
||||||
&plugin_env.wasi_env,
|
|
||||||
&serde_json::to_string(&key).unwrap(),
|
|
||||||
);
|
|
||||||
handler.call(&[]).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
drop(send_screen_instructions.send(ScreenInstruction::Render));
|
drop(send_screen_instructions.send(ScreenInstruction::Render));
|
||||||
}
|
|
||||||
PluginInstruction::Unload(pid) => drop(plugin_map.remove(&pid)),
|
|
||||||
PluginInstruction::Quit => break,
|
|
||||||
}
|
}
|
||||||
|
PluginInstruction::Unload(pid) => drop(plugin_map.remove(&pid)),
|
||||||
|
PluginInstruction::Quit => break,
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
.unwrap(),
|
})
|
||||||
);
|
.unwrap();
|
||||||
|
|
||||||
// TODO: currently we don't push this into active_threads
|
// TODO: currently we don't wait for this to quit
|
||||||
// because otherwise the app will hang. Need to fix this so it both
|
// because otherwise the app will hang. Need to fix this so it both
|
||||||
// listens to the ipc-bus and is able to quit cleanly
|
// listens to the ipc-bus and is able to quit cleanly
|
||||||
#[cfg(not(test))]
|
#[cfg(not(test))]
|
||||||
|
|
@ -663,8 +651,11 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
|
||||||
}
|
}
|
||||||
AppInstruction::Error(backtrace) => {
|
AppInstruction::Error(backtrace) => {
|
||||||
let _ = send_screen_instructions.send(ScreenInstruction::Quit);
|
let _ = send_screen_instructions.send(ScreenInstruction::Quit);
|
||||||
|
let _ = screen_thread.join();
|
||||||
let _ = send_pty_instructions.send(PtyInstruction::Quit);
|
let _ = send_pty_instructions.send(PtyInstruction::Quit);
|
||||||
|
let _ = pty_thread.join();
|
||||||
let _ = send_plugin_instructions.send(PluginInstruction::Quit);
|
let _ = send_plugin_instructions.send(PluginInstruction::Quit);
|
||||||
|
let _ = wasm_thread.join();
|
||||||
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);
|
||||||
|
|
@ -672,17 +663,17 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
|
||||||
.get_stdout_writer()
|
.get_stdout_writer()
|
||||||
.write(error.as_bytes())
|
.write(error.as_bytes())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
for thread_handler in active_threads {
|
|
||||||
let _ = thread_handler.join();
|
|
||||||
}
|
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for thread_handler in active_threads {
|
let _ = send_screen_instructions.send(ScreenInstruction::Quit);
|
||||||
thread_handler.join().unwrap();
|
screen_thread.join().unwrap();
|
||||||
}
|
let _ = send_pty_instructions.send(PtyInstruction::Quit);
|
||||||
|
pty_thread.join().unwrap();
|
||||||
|
let _ = send_plugin_instructions.send(PluginInstruction::Quit);
|
||||||
|
wasm_thread.join().unwrap();
|
||||||
|
|
||||||
// cleanup();
|
// cleanup();
|
||||||
let reset_style = "\u{1b}[m";
|
let reset_style = "\u{1b}[m";
|
||||||
|
|
|
||||||
|
|
@ -146,7 +146,9 @@ impl Screen {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
if self.tabs.is_empty() {
|
if self.tabs.is_empty() {
|
||||||
self.active_tab_index = None;
|
self.active_tab_index = None;
|
||||||
self.render();
|
self.send_app_instructions
|
||||||
|
.send(AppInstruction::Exit)
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn render(&mut self) {
|
pub fn render(&mut self) {
|
||||||
|
|
@ -156,10 +158,6 @@ impl Screen {
|
||||||
} else {
|
} else {
|
||||||
self.close_tab();
|
self.close_tab();
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
self.send_app_instructions
|
|
||||||
.send(AppInstruction::Exit)
|
|
||||||
.unwrap();
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue