Added the set_selectable plugin API function
This commit is contained in:
parent
7e308515e5
commit
efcd36a52c
11 changed files with 170 additions and 63 deletions
|
|
@ -166,6 +166,7 @@ pub enum ScreenContext {
|
||||||
ClearScroll,
|
ClearScroll,
|
||||||
CloseFocusedPane,
|
CloseFocusedPane,
|
||||||
ToggleActiveTerminalFullscreen,
|
ToggleActiveTerminalFullscreen,
|
||||||
|
SetSelectable,
|
||||||
ClosePane,
|
ClosePane,
|
||||||
ApplyLayout,
|
ApplyLayout,
|
||||||
NewTab,
|
NewTab,
|
||||||
|
|
@ -200,6 +201,7 @@ impl From<&ScreenInstruction> for ScreenContext {
|
||||||
ScreenInstruction::ToggleActiveTerminalFullscreen => {
|
ScreenInstruction::ToggleActiveTerminalFullscreen => {
|
||||||
ScreenContext::ToggleActiveTerminalFullscreen
|
ScreenContext::ToggleActiveTerminalFullscreen
|
||||||
}
|
}
|
||||||
|
ScreenInstruction::SetSelectable(..) => ScreenContext::SetSelectable,
|
||||||
ScreenInstruction::ClosePane(_) => ScreenContext::ClosePane,
|
ScreenInstruction::ClosePane(_) => ScreenContext::ClosePane,
|
||||||
ScreenInstruction::ApplyLayout(_) => ScreenContext::ApplyLayout,
|
ScreenInstruction::ApplyLayout(_) => ScreenContext::ApplyLayout,
|
||||||
ScreenInstruction::NewTab(_) => ScreenContext::NewTab,
|
ScreenInstruction::NewTab(_) => ScreenContext::NewTab,
|
||||||
|
|
@ -244,6 +246,7 @@ pub enum PluginContext {
|
||||||
Load,
|
Load,
|
||||||
Draw,
|
Draw,
|
||||||
Input,
|
Input,
|
||||||
|
GlobalInput,
|
||||||
Unload,
|
Unload,
|
||||||
Quit,
|
Quit,
|
||||||
}
|
}
|
||||||
|
|
@ -254,6 +257,7 @@ impl From<&PluginInstruction> for PluginContext {
|
||||||
PluginInstruction::Load(..) => PluginContext::Load,
|
PluginInstruction::Load(..) => PluginContext::Load,
|
||||||
PluginInstruction::Draw(..) => PluginContext::Draw,
|
PluginInstruction::Draw(..) => PluginContext::Draw,
|
||||||
PluginInstruction::Input(..) => PluginContext::Input,
|
PluginInstruction::Input(..) => PluginContext::Input,
|
||||||
|
PluginInstruction::GlobalInput(_) => PluginContext::GlobalInput,
|
||||||
PluginInstruction::Unload(_) => PluginContext::Unload,
|
PluginInstruction::Unload(_) => PluginContext::Unload,
|
||||||
PluginInstruction::Quit => PluginContext::Quit,
|
PluginInstruction::Quit => PluginContext::Quit,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
21
src/input.rs
21
src/input.rs
|
|
@ -1,9 +1,9 @@
|
||||||
/// Module for handling input
|
/// Module for handling input
|
||||||
use crate::errors::ContextType;
|
|
||||||
use crate::os_input_output::OsApi;
|
use crate::os_input_output::OsApi;
|
||||||
use crate::pty_bus::PtyInstruction;
|
use crate::pty_bus::PtyInstruction;
|
||||||
use crate::screen::ScreenInstruction;
|
use crate::screen::ScreenInstruction;
|
||||||
use crate::CommandIsExecuting;
|
use crate::CommandIsExecuting;
|
||||||
|
use crate::{errors::ContextType, wasm_vm::PluginInstruction};
|
||||||
use crate::{AppInstruction, SenderWithContext, OPENCALLS};
|
use crate::{AppInstruction, SenderWithContext, OPENCALLS};
|
||||||
|
|
||||||
struct InputHandler {
|
struct InputHandler {
|
||||||
|
|
@ -12,6 +12,7 @@ struct InputHandler {
|
||||||
command_is_executing: CommandIsExecuting,
|
command_is_executing: CommandIsExecuting,
|
||||||
send_screen_instructions: SenderWithContext<ScreenInstruction>,
|
send_screen_instructions: SenderWithContext<ScreenInstruction>,
|
||||||
send_pty_instructions: SenderWithContext<PtyInstruction>,
|
send_pty_instructions: SenderWithContext<PtyInstruction>,
|
||||||
|
send_plugin_instructions: SenderWithContext<PluginInstruction>,
|
||||||
send_app_instructions: SenderWithContext<AppInstruction>,
|
send_app_instructions: SenderWithContext<AppInstruction>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -21,6 +22,7 @@ impl InputHandler {
|
||||||
command_is_executing: CommandIsExecuting,
|
command_is_executing: CommandIsExecuting,
|
||||||
send_screen_instructions: SenderWithContext<ScreenInstruction>,
|
send_screen_instructions: SenderWithContext<ScreenInstruction>,
|
||||||
send_pty_instructions: SenderWithContext<PtyInstruction>,
|
send_pty_instructions: SenderWithContext<PtyInstruction>,
|
||||||
|
send_plugin_instructions: SenderWithContext<PluginInstruction>,
|
||||||
send_app_instructions: SenderWithContext<AppInstruction>,
|
send_app_instructions: SenderWithContext<AppInstruction>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
InputHandler {
|
InputHandler {
|
||||||
|
|
@ -29,6 +31,7 @@ impl InputHandler {
|
||||||
command_is_executing,
|
command_is_executing,
|
||||||
send_screen_instructions,
|
send_screen_instructions,
|
||||||
send_pty_instructions,
|
send_pty_instructions,
|
||||||
|
send_plugin_instructions,
|
||||||
send_app_instructions,
|
send_app_instructions,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -38,6 +41,7 @@ impl InputHandler {
|
||||||
let mut err_ctx = OPENCALLS.with(|ctx| *ctx.borrow());
|
let mut err_ctx = OPENCALLS.with(|ctx| *ctx.borrow());
|
||||||
err_ctx.add_call(ContextType::StdinHandler);
|
err_ctx.add_call(ContextType::StdinHandler);
|
||||||
self.send_pty_instructions.update(err_ctx);
|
self.send_pty_instructions.update(err_ctx);
|
||||||
|
self.send_plugin_instructions.update(err_ctx);
|
||||||
self.send_app_instructions.update(err_ctx);
|
self.send_app_instructions.update(err_ctx);
|
||||||
self.send_screen_instructions.update(err_ctx);
|
self.send_screen_instructions.update(err_ctx);
|
||||||
loop {
|
loop {
|
||||||
|
|
@ -59,6 +63,11 @@ impl InputHandler {
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let stdin_buffer = self.os_input.read_from_stdin();
|
let stdin_buffer = self.os_input.read_from_stdin();
|
||||||
|
#[cfg(not(test))] // Absolutely zero clue why this breaks *all* of the tests
|
||||||
|
drop(
|
||||||
|
self.send_plugin_instructions
|
||||||
|
.send(PluginInstruction::GlobalInput(stdin_buffer.clone())),
|
||||||
|
);
|
||||||
match stdin_buffer.as_slice() {
|
match stdin_buffer.as_slice() {
|
||||||
[7] => {
|
[7] => {
|
||||||
// ctrl-g
|
// ctrl-g
|
||||||
|
|
@ -88,6 +97,11 @@ impl InputHandler {
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let stdin_buffer = self.os_input.read_from_stdin();
|
let stdin_buffer = self.os_input.read_from_stdin();
|
||||||
|
#[cfg(not(test))] // Absolutely zero clue why this breaks *all* of the tests
|
||||||
|
drop(
|
||||||
|
self.send_plugin_instructions
|
||||||
|
.send(PluginInstruction::GlobalInput(stdin_buffer.clone())),
|
||||||
|
);
|
||||||
// uncomment this to print the entered character to a log file (/tmp/mosaic/mosaic-log.txt) for debugging
|
// uncomment this to print the entered character to a log file (/tmp/mosaic/mosaic-log.txt) for debugging
|
||||||
// debug_log_to_file(format!("buffer {:?}", stdin_buffer));
|
// debug_log_to_file(format!("buffer {:?}", stdin_buffer));
|
||||||
|
|
||||||
|
|
@ -267,6 +281,9 @@ impl InputHandler {
|
||||||
self.send_pty_instructions
|
self.send_pty_instructions
|
||||||
.send(PtyInstruction::Quit)
|
.send(PtyInstruction::Quit)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
self.send_plugin_instructions
|
||||||
|
.send(PluginInstruction::Quit)
|
||||||
|
.unwrap();
|
||||||
self.send_app_instructions
|
self.send_app_instructions
|
||||||
.send(AppInstruction::Exit)
|
.send(AppInstruction::Exit)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
@ -297,6 +314,7 @@ pub fn input_loop(
|
||||||
command_is_executing: CommandIsExecuting,
|
command_is_executing: CommandIsExecuting,
|
||||||
send_screen_instructions: SenderWithContext<ScreenInstruction>,
|
send_screen_instructions: SenderWithContext<ScreenInstruction>,
|
||||||
send_pty_instructions: SenderWithContext<PtyInstruction>,
|
send_pty_instructions: SenderWithContext<PtyInstruction>,
|
||||||
|
send_plugin_instructions: SenderWithContext<PluginInstruction>,
|
||||||
send_app_instructions: SenderWithContext<AppInstruction>,
|
send_app_instructions: SenderWithContext<AppInstruction>,
|
||||||
) {
|
) {
|
||||||
let _handler = InputHandler::new(
|
let _handler = InputHandler::new(
|
||||||
|
|
@ -304,6 +322,7 @@ pub fn input_loop(
|
||||||
command_is_executing,
|
command_is_executing,
|
||||||
send_screen_instructions,
|
send_screen_instructions,
|
||||||
send_pty_instructions,
|
send_pty_instructions,
|
||||||
|
send_plugin_instructions,
|
||||||
send_app_instructions,
|
send_app_instructions,
|
||||||
)
|
)
|
||||||
.get_input();
|
.get_input();
|
||||||
|
|
|
||||||
47
src/main.rs
47
src/main.rs
|
|
@ -355,6 +355,14 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
|
||||||
screen.get_active_tab_mut().unwrap().close_focused_pane();
|
screen.get_active_tab_mut().unwrap().close_focused_pane();
|
||||||
screen.render();
|
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) => {
|
ScreenInstruction::ClosePane(id) => {
|
||||||
screen.get_active_tab_mut().unwrap().close_pane(id);
|
screen.get_active_tab_mut().unwrap().close_pane(id);
|
||||||
screen.render();
|
screen.render();
|
||||||
|
|
@ -430,7 +438,9 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
|
||||||
let wasi = wasi_env.import_object(&module).unwrap();
|
let wasi = wasi_env.import_object(&module).unwrap();
|
||||||
|
|
||||||
let plugin_env = PluginEnv {
|
let plugin_env = PluginEnv {
|
||||||
|
plugin_id,
|
||||||
send_pty_instructions: send_pty_instructions.clone(),
|
send_pty_instructions: send_pty_instructions.clone(),
|
||||||
|
send_screen_instructions: send_screen_instructions.clone(),
|
||||||
wasi_env,
|
wasi_env,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -457,15 +467,29 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
|
||||||
|
|
||||||
buf_tx.send(wasi_stdout(&plugin_env.wasi_env)).unwrap();
|
buf_tx.send(wasi_stdout(&plugin_env.wasi_env)).unwrap();
|
||||||
}
|
}
|
||||||
|
// FIXME: Deduplicate this with the callback below!
|
||||||
PluginInstruction::Input(pid, input_bytes) => {
|
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() {
|
||||||
|
if let Ok(key) = key {
|
||||||
|
wasi_write_string(
|
||||||
|
&plugin_env.wasi_env,
|
||||||
|
&serde_json::to_string(&key).unwrap(),
|
||||||
|
);
|
||||||
|
handle_key.call(&[]).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
drop(send_screen_instructions.send(ScreenInstruction::Render));
|
||||||
|
}
|
||||||
|
PluginInstruction::GlobalInput(input_bytes) => {
|
||||||
// FIXME: Set up an event subscription system, and timed callbacks
|
// FIXME: Set up an event subscription system, and timed callbacks
|
||||||
for (&id, (instance, plugin_env)) in &plugin_map {
|
for (instance, plugin_env) in plugin_map.values() {
|
||||||
let handler = if PaneId::Plugin(id) == pid {
|
let handler =
|
||||||
"handle_key"
|
instance.exports.get_function("handle_global_key").unwrap();
|
||||||
} else {
|
|
||||||
"handle_global_key"
|
|
||||||
};
|
|
||||||
let handler = instance.exports.get_function(handler).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(
|
||||||
|
|
@ -477,9 +501,7 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
send_screen_instructions
|
drop(send_screen_instructions.send(ScreenInstruction::Render));
|
||||||
.send(ScreenInstruction::Render)
|
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
PluginInstruction::Unload(pid) => drop(plugin_map.remove(&pid)),
|
PluginInstruction::Unload(pid) => drop(plugin_map.remove(&pid)),
|
||||||
PluginInstruction::Quit => break,
|
PluginInstruction::Quit => break,
|
||||||
|
|
@ -556,6 +578,7 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
|
||||||
.spawn({
|
.spawn({
|
||||||
let send_screen_instructions = send_screen_instructions.clone();
|
let send_screen_instructions = send_screen_instructions.clone();
|
||||||
let send_pty_instructions = send_pty_instructions.clone();
|
let send_pty_instructions = send_pty_instructions.clone();
|
||||||
|
let send_plugin_instructions = send_plugin_instructions.clone();
|
||||||
let os_input = os_input.clone();
|
let os_input = os_input.clone();
|
||||||
move || {
|
move || {
|
||||||
input_loop(
|
input_loop(
|
||||||
|
|
@ -563,6 +586,7 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
|
||||||
command_is_executing,
|
command_is_executing,
|
||||||
send_screen_instructions,
|
send_screen_instructions,
|
||||||
send_pty_instructions,
|
send_pty_instructions,
|
||||||
|
send_plugin_instructions,
|
||||||
send_app_instructions,
|
send_app_instructions,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -581,14 +605,12 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
|
||||||
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);
|
||||||
|
|
||||||
let _ = send_plugin_instructions.send(PluginInstruction::Quit);
|
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);
|
||||||
|
|
||||||
let _ = send_plugin_instructions.send(PluginInstruction::Quit);
|
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);
|
||||||
|
|
@ -608,6 +630,7 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
|
||||||
for thread_handler in active_threads {
|
for thread_handler in active_threads {
|
||||||
thread_handler.join().unwrap();
|
thread_handler.join().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
// cleanup();
|
// cleanup();
|
||||||
let reset_style = "\u{1b}[m";
|
let reset_style = "\u{1b}[m";
|
||||||
let show_cursor = "\u{1b}[?25h";
|
let show_cursor = "\u{1b}[?25h";
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ use crate::panes::{PaneId, PositionAndSize};
|
||||||
pub struct PluginPane {
|
pub struct PluginPane {
|
||||||
pub pid: u32,
|
pub pid: u32,
|
||||||
pub should_render: bool,
|
pub should_render: bool,
|
||||||
|
pub selectable: bool,
|
||||||
pub position_and_size: PositionAndSize,
|
pub position_and_size: PositionAndSize,
|
||||||
pub position_and_size_override: Option<PositionAndSize>,
|
pub position_and_size_override: Option<PositionAndSize>,
|
||||||
pub send_plugin_instructions: SenderWithContext<PluginInstruction>,
|
pub send_plugin_instructions: SenderWithContext<PluginInstruction>,
|
||||||
|
|
@ -23,6 +24,7 @@ impl PluginPane {
|
||||||
Self {
|
Self {
|
||||||
pid,
|
pid,
|
||||||
should_render: true,
|
should_render: true,
|
||||||
|
selectable: true,
|
||||||
position_and_size,
|
position_and_size,
|
||||||
position_and_size_override: None,
|
position_and_size_override: None,
|
||||||
send_plugin_instructions,
|
send_plugin_instructions,
|
||||||
|
|
@ -92,6 +94,12 @@ impl Pane for PluginPane {
|
||||||
fn set_should_render(&mut self, should_render: bool) {
|
fn set_should_render(&mut self, should_render: bool) {
|
||||||
self.should_render = should_render;
|
self.should_render = should_render;
|
||||||
}
|
}
|
||||||
|
fn selectable(&self) -> bool {
|
||||||
|
self.selectable
|
||||||
|
}
|
||||||
|
fn set_selectable(&mut self, selectable: bool) {
|
||||||
|
self.selectable = selectable;
|
||||||
|
}
|
||||||
fn render(&mut self) -> Option<String> {
|
fn render(&mut self) -> Option<String> {
|
||||||
// if self.should_render {
|
// if self.should_render {
|
||||||
if true {
|
if true {
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@ pub struct TerminalPane {
|
||||||
pub pid: RawFd,
|
pub pid: RawFd,
|
||||||
pub scroll: Scroll,
|
pub scroll: Scroll,
|
||||||
pub should_render: bool,
|
pub should_render: bool,
|
||||||
|
pub selectable: bool,
|
||||||
pub position_and_size: PositionAndSize,
|
pub position_and_size: PositionAndSize,
|
||||||
pub position_and_size_override: Option<PositionAndSize>,
|
pub position_and_size_override: Option<PositionAndSize>,
|
||||||
pub cursor_key_mode: bool, // DECCKM - when set, cursor keys should send ANSI direction codes (eg. "OD") instead of the arrow keys (eg. "[D")
|
pub cursor_key_mode: bool, // DECCKM - when set, cursor keys should send ANSI direction codes (eg. "OD") instead of the arrow keys (eg. "[D")
|
||||||
|
|
@ -165,6 +166,12 @@ impl Pane for TerminalPane {
|
||||||
fn set_should_render(&mut self, should_render: bool) {
|
fn set_should_render(&mut self, should_render: bool) {
|
||||||
self.should_render = should_render;
|
self.should_render = should_render;
|
||||||
}
|
}
|
||||||
|
fn selectable(&self) -> bool {
|
||||||
|
self.selectable
|
||||||
|
}
|
||||||
|
fn set_selectable(&mut self, selectable: bool) {
|
||||||
|
self.selectable = selectable;
|
||||||
|
}
|
||||||
fn render(&mut self) -> Option<String> {
|
fn render(&mut self) -> Option<String> {
|
||||||
// if self.should_render {
|
// if self.should_render {
|
||||||
if true {
|
if true {
|
||||||
|
|
@ -274,6 +281,7 @@ impl TerminalPane {
|
||||||
pid,
|
pid,
|
||||||
scroll,
|
scroll,
|
||||||
should_render: true,
|
should_render: true,
|
||||||
|
selectable: true,
|
||||||
pending_styles,
|
pending_styles,
|
||||||
position_and_size,
|
position_and_size,
|
||||||
position_and_size_override: None,
|
position_and_size_override: None,
|
||||||
|
|
|
||||||
|
|
@ -299,13 +299,15 @@ impl PtyBus {
|
||||||
let child_pid = self.id_to_child_pid.get(&id).unwrap();
|
let child_pid = self.id_to_child_pid.get(&id).unwrap();
|
||||||
self.os_input.kill(*child_pid).unwrap();
|
self.os_input.kill(*child_pid).unwrap();
|
||||||
}
|
}
|
||||||
PaneId::Plugin(pid) => self
|
PaneId::Plugin(pid) => drop(
|
||||||
.send_plugin_instructions
|
self.send_plugin_instructions
|
||||||
.send(PluginInstruction::Unload(pid))
|
.send(PluginInstruction::Unload(pid)),
|
||||||
.unwrap(),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn close_tab(&mut self, ids: Vec<PaneId>) {
|
pub fn close_tab(&mut self, ids: Vec<PaneId>) {
|
||||||
ids.iter().for_each(|&id| self.close_pane(id));
|
ids.iter().for_each(|&id| {
|
||||||
|
self.close_pane(id);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,7 @@ pub enum ScreenInstruction {
|
||||||
ClearScroll,
|
ClearScroll,
|
||||||
CloseFocusedPane,
|
CloseFocusedPane,
|
||||||
ToggleActiveTerminalFullscreen,
|
ToggleActiveTerminalFullscreen,
|
||||||
|
SetSelectable(PaneId, bool),
|
||||||
ClosePane(PaneId),
|
ClosePane(PaneId),
|
||||||
ApplyLayout((Layout, Vec<RawFd>)),
|
ApplyLayout((Layout, Vec<RawFd>)),
|
||||||
NewTab(RawFd),
|
NewTab(RawFd),
|
||||||
|
|
@ -138,7 +139,7 @@ impl Screen {
|
||||||
if self.tabs.len() > 1 {
|
if self.tabs.len() > 1 {
|
||||||
self.switch_tab_prev();
|
self.switch_tab_prev();
|
||||||
}
|
}
|
||||||
let mut active_tab = self.tabs.remove(&active_tab_index).unwrap();
|
let active_tab = self.tabs.remove(&active_tab_index).unwrap();
|
||||||
let pane_ids = active_tab.get_pane_ids();
|
let pane_ids = active_tab.get_pane_ids();
|
||||||
self.send_pty_instructions
|
self.send_pty_instructions
|
||||||
.send(PtyInstruction::CloseTab(pane_ids))
|
.send(PtyInstruction::CloseTab(pane_ids))
|
||||||
|
|
|
||||||
105
src/tab.rs
105
src/tab.rs
|
|
@ -66,6 +66,7 @@ pub struct Tab {
|
||||||
pub send_app_instructions: SenderWithContext<AppInstruction>,
|
pub send_app_instructions: SenderWithContext<AppInstruction>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: Use a struct that has a pane_type enum, to reduce all of the duplication
|
||||||
pub trait Pane {
|
pub trait Pane {
|
||||||
fn x(&self) -> usize;
|
fn x(&self) -> usize;
|
||||||
fn y(&self) -> usize;
|
fn y(&self) -> usize;
|
||||||
|
|
@ -81,6 +82,8 @@ pub trait Pane {
|
||||||
fn position_and_size_override(&self) -> Option<PositionAndSize>;
|
fn position_and_size_override(&self) -> Option<PositionAndSize>;
|
||||||
fn should_render(&self) -> bool;
|
fn should_render(&self) -> bool;
|
||||||
fn set_should_render(&mut self, should_render: bool);
|
fn set_should_render(&mut self, should_render: bool);
|
||||||
|
fn selectable(&self) -> bool;
|
||||||
|
fn set_selectable(&mut self, selectable: bool);
|
||||||
fn render(&mut self) -> Option<String>;
|
fn render(&mut self) -> Option<String>;
|
||||||
fn pid(&self) -> PaneId;
|
fn pid(&self) -> PaneId;
|
||||||
fn reduce_height_down(&mut self, count: usize);
|
fn reduce_height_down(&mut self, count: usize);
|
||||||
|
|
@ -473,23 +476,23 @@ impl Tab {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn write_to_active_terminal(&mut self, input_bytes: Vec<u8>) {
|
pub fn write_to_active_terminal(&mut self, input_bytes: Vec<u8>) {
|
||||||
let pid = self.get_active_pane_id();
|
match self.get_active_pane_id() {
|
||||||
|
Some(PaneId::Terminal(active_terminal_id)) => {
|
||||||
if let Some(pid) = pid {
|
let active_terminal = self.get_active_pane().unwrap();
|
||||||
self.send_plugin_instructions
|
let mut adjusted_input = active_terminal.adjust_input_to_terminal(input_bytes);
|
||||||
.send(PluginInstruction::Input(pid, input_bytes.clone()))
|
self.os_api
|
||||||
.unwrap();
|
.write_to_tty_stdin(active_terminal_id, &mut adjusted_input)
|
||||||
}
|
.expect("failed to write to terminal");
|
||||||
|
self.os_api
|
||||||
if let Some(PaneId::Terminal(active_terminal_id)) = pid {
|
.tcdrain(active_terminal_id)
|
||||||
let active_terminal = self.get_active_pane().unwrap();
|
.expect("failed to drain terminal");
|
||||||
let mut adjusted_input = active_terminal.adjust_input_to_terminal(input_bytes);
|
}
|
||||||
self.os_api
|
Some(PaneId::Plugin(pid)) => {
|
||||||
.write_to_tty_stdin(active_terminal_id, &mut adjusted_input)
|
self.send_plugin_instructions
|
||||||
.expect("failed to write to terminal");
|
.send(PluginInstruction::Input(pid, input_bytes))
|
||||||
self.os_api
|
.unwrap();
|
||||||
.tcdrain(active_terminal_id)
|
}
|
||||||
.expect("failed to drain terminal");
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn get_active_terminal_cursor_position(&self) -> Option<(usize, usize)> {
|
pub fn get_active_terminal_cursor_position(&self) -> Option<(usize, usize)> {
|
||||||
|
|
@ -618,10 +621,24 @@ impl Tab {
|
||||||
fn get_panes(&self) -> impl Iterator<Item = (&PaneId, &Box<dyn Pane>)> {
|
fn get_panes(&self) -> impl Iterator<Item = (&PaneId, &Box<dyn Pane>)> {
|
||||||
self.panes.iter()
|
self.panes.iter()
|
||||||
}
|
}
|
||||||
|
// FIXME: This is some shameful duplication...
|
||||||
|
fn get_selectable_panes(&self) -> impl Iterator<Item = (&PaneId, &Box<dyn Pane>)> {
|
||||||
|
self.panes.iter().filter(|(_, p)| p.selectable())
|
||||||
|
}
|
||||||
fn has_panes(&self) -> bool {
|
fn has_panes(&self) -> bool {
|
||||||
let mut all_terminals = self.get_panes();
|
let mut all_terminals = self.get_panes();
|
||||||
all_terminals.next().is_some()
|
all_terminals.next().is_some()
|
||||||
}
|
}
|
||||||
|
fn has_selectable_panes(&self) -> bool {
|
||||||
|
let mut all_terminals = self.get_selectable_panes();
|
||||||
|
all_terminals.next().is_some()
|
||||||
|
}
|
||||||
|
fn next_active_pane(&self, panes: Vec<PaneId>) -> Option<PaneId> {
|
||||||
|
panes
|
||||||
|
.into_iter()
|
||||||
|
.rev()
|
||||||
|
.find(|pid| self.panes.get(pid).unwrap().selectable())
|
||||||
|
}
|
||||||
fn pane_ids_directly_left_of(&self, id: &PaneId) -> Option<Vec<PaneId>> {
|
fn pane_ids_directly_left_of(&self, id: &PaneId) -> Option<Vec<PaneId>> {
|
||||||
let mut ids = vec![];
|
let mut ids = vec![];
|
||||||
let terminal_to_check = self.panes.get(id).unwrap();
|
let terminal_to_check = self.panes.get(id).unwrap();
|
||||||
|
|
@ -1421,14 +1438,14 @@ impl Tab {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn move_focus(&mut self) {
|
pub fn move_focus(&mut self) {
|
||||||
if !self.has_panes() {
|
if !self.has_selectable_panes() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if self.fullscreen_is_active {
|
if self.fullscreen_is_active {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let active_terminal_id = self.get_active_pane_id().unwrap();
|
let active_terminal_id = self.get_active_pane_id().unwrap();
|
||||||
let terminal_ids: Vec<PaneId> = self.get_panes().map(|(&pid, _)| pid).collect(); // TODO: better, no allocations
|
let terminal_ids: Vec<PaneId> = self.get_selectable_panes().map(|(&pid, _)| pid).collect(); // TODO: better, no allocations
|
||||||
let first_terminal = terminal_ids.get(0).unwrap();
|
let first_terminal = terminal_ids.get(0).unwrap();
|
||||||
let active_terminal_id_position = terminal_ids
|
let active_terminal_id_position = terminal_ids
|
||||||
.iter()
|
.iter()
|
||||||
|
|
@ -1442,7 +1459,7 @@ impl Tab {
|
||||||
self.render();
|
self.render();
|
||||||
}
|
}
|
||||||
pub fn move_focus_left(&mut self) {
|
pub fn move_focus_left(&mut self) {
|
||||||
if !self.has_panes() {
|
if !self.has_selectable_panes() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if self.fullscreen_is_active {
|
if self.fullscreen_is_active {
|
||||||
|
|
@ -1450,7 +1467,7 @@ impl Tab {
|
||||||
}
|
}
|
||||||
let active_terminal = self.get_active_pane();
|
let active_terminal = self.get_active_pane();
|
||||||
if let Some(active) = active_terminal {
|
if let Some(active) = active_terminal {
|
||||||
let terminals = self.get_panes();
|
let terminals = self.get_selectable_panes();
|
||||||
let next_index = terminals
|
let next_index = terminals
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.filter(|(_, (_, c))| {
|
.filter(|(_, (_, c))| {
|
||||||
|
|
@ -1472,7 +1489,7 @@ impl Tab {
|
||||||
self.render();
|
self.render();
|
||||||
}
|
}
|
||||||
pub fn move_focus_down(&mut self) {
|
pub fn move_focus_down(&mut self) {
|
||||||
if !self.has_panes() {
|
if !self.has_selectable_panes() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if self.fullscreen_is_active {
|
if self.fullscreen_is_active {
|
||||||
|
|
@ -1480,7 +1497,7 @@ impl Tab {
|
||||||
}
|
}
|
||||||
let active_terminal = self.get_active_pane();
|
let active_terminal = self.get_active_pane();
|
||||||
if let Some(active) = active_terminal {
|
if let Some(active) = active_terminal {
|
||||||
let terminals = self.get_panes();
|
let terminals = self.get_selectable_panes();
|
||||||
let next_index = terminals
|
let next_index = terminals
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.filter(|(_, (_, c))| {
|
.filter(|(_, (_, c))| {
|
||||||
|
|
@ -1502,7 +1519,7 @@ impl Tab {
|
||||||
self.render();
|
self.render();
|
||||||
}
|
}
|
||||||
pub fn move_focus_up(&mut self) {
|
pub fn move_focus_up(&mut self) {
|
||||||
if !self.has_panes() {
|
if !self.has_selectable_panes() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if self.fullscreen_is_active {
|
if self.fullscreen_is_active {
|
||||||
|
|
@ -1510,7 +1527,7 @@ impl Tab {
|
||||||
}
|
}
|
||||||
let active_terminal = self.get_active_pane();
|
let active_terminal = self.get_active_pane();
|
||||||
if let Some(active) = active_terminal {
|
if let Some(active) = active_terminal {
|
||||||
let terminals = self.get_panes();
|
let terminals = self.get_selectable_panes();
|
||||||
let next_index = terminals
|
let next_index = terminals
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.filter(|(_, (_, c))| {
|
.filter(|(_, (_, c))| {
|
||||||
|
|
@ -1532,7 +1549,7 @@ impl Tab {
|
||||||
self.render();
|
self.render();
|
||||||
}
|
}
|
||||||
pub fn move_focus_right(&mut self) {
|
pub fn move_focus_right(&mut self) {
|
||||||
if !self.has_panes() {
|
if !self.has_selectable_panes() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if self.fullscreen_is_active {
|
if self.fullscreen_is_active {
|
||||||
|
|
@ -1540,7 +1557,7 @@ impl Tab {
|
||||||
}
|
}
|
||||||
let active_terminal = self.get_active_pane();
|
let active_terminal = self.get_active_pane();
|
||||||
if let Some(active) = active_terminal {
|
if let Some(active) = active_terminal {
|
||||||
let terminals = self.get_panes();
|
let terminals = self.get_selectable_panes();
|
||||||
let next_index = terminals
|
let next_index = terminals
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.filter(|(_, (_, c))| {
|
.filter(|(_, (_, c))| {
|
||||||
|
|
@ -1578,7 +1595,7 @@ impl Tab {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
fn panes_to_the_left_between_aligning_borders(&self, id: PaneId) -> Option<Vec<PaneId>> {
|
fn panes_to_the_left_between_aligning_borders(&self, id: PaneId) -> Option<Vec<PaneId>> {
|
||||||
if let Some(terminal) = &self.panes.get(&id) {
|
if let Some(terminal) = self.panes.get(&id) {
|
||||||
let upper_close_border = terminal.y();
|
let upper_close_border = terminal.y();
|
||||||
let lower_close_border = terminal.y() + terminal.rows() + 1;
|
let lower_close_border = terminal.y() + terminal.rows() + 1;
|
||||||
|
|
||||||
|
|
@ -1601,7 +1618,7 @@ impl Tab {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
fn panes_to_the_right_between_aligning_borders(&self, id: PaneId) -> Option<Vec<PaneId>> {
|
fn panes_to_the_right_between_aligning_borders(&self, id: PaneId) -> Option<Vec<PaneId>> {
|
||||||
if let Some(terminal) = &self.panes.get(&id) {
|
if let Some(terminal) = self.panes.get(&id) {
|
||||||
let upper_close_border = terminal.y();
|
let upper_close_border = terminal.y();
|
||||||
let lower_close_border = terminal.y() + terminal.rows() + 1;
|
let lower_close_border = terminal.y() + terminal.rows() + 1;
|
||||||
|
|
||||||
|
|
@ -1625,7 +1642,7 @@ impl Tab {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
fn panes_above_between_aligning_borders(&self, id: PaneId) -> Option<Vec<PaneId>> {
|
fn panes_above_between_aligning_borders(&self, id: PaneId) -> Option<Vec<PaneId>> {
|
||||||
if let Some(terminal) = &self.panes.get(&id) {
|
if let Some(terminal) = self.panes.get(&id) {
|
||||||
let left_close_border = terminal.x();
|
let left_close_border = terminal.x();
|
||||||
let right_close_border = terminal.x() + terminal.columns() + 1;
|
let right_close_border = terminal.x() + terminal.columns() + 1;
|
||||||
|
|
||||||
|
|
@ -1647,8 +1664,8 @@ impl Tab {
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
fn terminals_below_between_aligning_borders(&self, id: PaneId) -> Option<Vec<PaneId>> {
|
fn panes_below_between_aligning_borders(&self, id: PaneId) -> Option<Vec<PaneId>> {
|
||||||
if let Some(terminal) = &self.panes.get(&id) {
|
if let Some(terminal) = self.panes.get(&id) {
|
||||||
let left_close_border = terminal.x();
|
let left_close_border = terminal.x();
|
||||||
let right_close_border = terminal.x() + terminal.columns() + 1;
|
let right_close_border = terminal.x() + terminal.columns() + 1;
|
||||||
|
|
||||||
|
|
@ -1681,9 +1698,17 @@ impl Tab {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn get_pane_ids(&mut self) -> Vec<PaneId> {
|
pub fn get_pane_ids(&self) -> Vec<PaneId> {
|
||||||
self.get_panes().map(|(&pid, _)| pid).collect()
|
self.get_panes().map(|(&pid, _)| pid).collect()
|
||||||
}
|
}
|
||||||
|
pub fn set_pane_selectable(&mut self, id: PaneId, selectable: bool) {
|
||||||
|
if let Some(pane) = self.panes.get_mut(&id) {
|
||||||
|
pane.set_selectable(selectable);
|
||||||
|
if self.get_active_pane_id() == Some(id) && !selectable {
|
||||||
|
self.active_terminal = self.next_active_pane(self.get_pane_ids())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
pub fn close_pane(&mut self, id: PaneId) {
|
pub fn close_pane(&mut self, id: PaneId) {
|
||||||
if self.panes.get(&id).is_some() {
|
if self.panes.get(&id).is_some() {
|
||||||
self.close_pane_without_rerender(id);
|
self.close_pane_without_rerender(id);
|
||||||
|
|
@ -1699,7 +1724,7 @@ impl Tab {
|
||||||
// 1 for the border
|
// 1 for the border
|
||||||
}
|
}
|
||||||
if self.active_terminal == Some(id) {
|
if self.active_terminal == Some(id) {
|
||||||
self.active_terminal = Some(*terminals.last().unwrap());
|
self.active_terminal = self.next_active_pane(terminals);
|
||||||
}
|
}
|
||||||
} else if let Some(terminals) = self.panes_to_the_right_between_aligning_borders(id) {
|
} else if let Some(terminals) = self.panes_to_the_right_between_aligning_borders(id) {
|
||||||
for terminal_id in terminals.iter() {
|
for terminal_id in terminals.iter() {
|
||||||
|
|
@ -1707,7 +1732,7 @@ impl Tab {
|
||||||
// 1 for the border
|
// 1 for the border
|
||||||
}
|
}
|
||||||
if self.active_terminal == Some(id) {
|
if self.active_terminal == Some(id) {
|
||||||
self.active_terminal = Some(*terminals.last().unwrap());
|
self.active_terminal = self.next_active_pane(terminals);
|
||||||
}
|
}
|
||||||
} else if let Some(terminals) = self.panes_above_between_aligning_borders(id) {
|
} else if let Some(terminals) = self.panes_above_between_aligning_borders(id) {
|
||||||
for terminal_id in terminals.iter() {
|
for terminal_id in terminals.iter() {
|
||||||
|
|
@ -1715,21 +1740,21 @@ impl Tab {
|
||||||
// 1 for the border
|
// 1 for the border
|
||||||
}
|
}
|
||||||
if self.active_terminal == Some(id) {
|
if self.active_terminal == Some(id) {
|
||||||
self.active_terminal = Some(*terminals.last().unwrap());
|
self.active_terminal = self.next_active_pane(terminals);
|
||||||
}
|
}
|
||||||
} else if let Some(terminals) = self.terminals_below_between_aligning_borders(id) {
|
} else if let Some(terminals) = self.panes_below_between_aligning_borders(id) {
|
||||||
for terminal_id in terminals.iter() {
|
for terminal_id in terminals.iter() {
|
||||||
self.increase_pane_height_up(&terminal_id, terminal_to_close_height + 1);
|
self.increase_pane_height_up(&terminal_id, terminal_to_close_height + 1);
|
||||||
// 1 for the border
|
// 1 for the border
|
||||||
}
|
}
|
||||||
if self.active_terminal == Some(id) {
|
if self.active_terminal == Some(id) {
|
||||||
self.active_terminal = Some(*terminals.last().unwrap());
|
self.active_terminal = self.next_active_pane(terminals);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
}
|
}
|
||||||
self.panes.remove(&id);
|
self.panes.remove(&id);
|
||||||
if !self.has_panes() {
|
if self.active_terminal.is_none() {
|
||||||
self.active_terminal = None;
|
self.active_terminal = self.next_active_pane(self.get_pane_ids());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,3 +11,4 @@ parts:
|
||||||
- direction: Vertical
|
- direction: Vertical
|
||||||
split_size:
|
split_size:
|
||||||
Fixed: 1
|
Fixed: 1
|
||||||
|
plugin: status-bar.wasm
|
||||||
|
|
@ -2,19 +2,22 @@ use std::{path::PathBuf, sync::mpsc::Sender};
|
||||||
use wasmer::{imports, Function, ImportObject, Store, WasmerEnv};
|
use wasmer::{imports, Function, ImportObject, Store, WasmerEnv};
|
||||||
use wasmer_wasi::WasiEnv;
|
use wasmer_wasi::WasiEnv;
|
||||||
|
|
||||||
use crate::{panes::PaneId, pty_bus::PtyInstruction, SenderWithContext};
|
use crate::{panes::PaneId, pty_bus::PtyInstruction, screen::ScreenInstruction, SenderWithContext};
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum PluginInstruction {
|
pub enum PluginInstruction {
|
||||||
Load(Sender<u32>, PathBuf),
|
Load(Sender<u32>, PathBuf),
|
||||||
Draw(Sender<String>, u32, usize, usize), // String buffer, plugin id, rows, cols
|
Draw(Sender<String>, u32, usize, usize), // String buffer, plugin id, rows, cols
|
||||||
Input(PaneId, Vec<u8>), // pane id, input bytes
|
Input(u32, Vec<u8>), // plugin id, input bytes
|
||||||
|
GlobalInput(Vec<u8>), // input bytes
|
||||||
Unload(u32),
|
Unload(u32),
|
||||||
Quit,
|
Quit,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(WasmerEnv, Clone)]
|
#[derive(WasmerEnv, Clone)]
|
||||||
pub struct PluginEnv {
|
pub struct PluginEnv {
|
||||||
|
pub plugin_id: u32,
|
||||||
|
pub send_screen_instructions: SenderWithContext<ScreenInstruction>,
|
||||||
pub send_pty_instructions: SenderWithContext<PtyInstruction>, // FIXME: This should be a big bundle of all of the channels
|
pub send_pty_instructions: SenderWithContext<PtyInstruction>, // FIXME: This should be a big bundle of all of the channels
|
||||||
pub wasi_env: WasiEnv,
|
pub wasi_env: WasiEnv,
|
||||||
}
|
}
|
||||||
|
|
@ -24,7 +27,8 @@ pub struct PluginEnv {
|
||||||
pub fn mosaic_imports(store: &Store, plugin_env: &PluginEnv) -> ImportObject {
|
pub fn mosaic_imports(store: &Store, plugin_env: &PluginEnv) -> ImportObject {
|
||||||
imports! {
|
imports! {
|
||||||
"mosaic" => {
|
"mosaic" => {
|
||||||
"host_open_file" => Function::new_native_with_env(store, plugin_env.clone(), host_open_file)
|
"host_open_file" => Function::new_native_with_env(store, plugin_env.clone(), host_open_file),
|
||||||
|
"host_set_selectable" => Function::new_native_with_env(store, plugin_env.clone(), host_set_selectable),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -38,6 +42,18 @@ fn host_open_file(plugin_env: &PluginEnv) {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: Think about these naming conventions – should everything be prefixed by 'host'?
|
||||||
|
fn host_set_selectable(plugin_env: &PluginEnv, selectable: i32) {
|
||||||
|
let selectable = selectable != 0;
|
||||||
|
plugin_env
|
||||||
|
.send_screen_instructions
|
||||||
|
.send(ScreenInstruction::SetSelectable(
|
||||||
|
PaneId::Plugin(plugin_env.plugin_id),
|
||||||
|
selectable,
|
||||||
|
))
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
// Helper Functions ---------------------------------------------------------------------------------------------------
|
// Helper Functions ---------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
// FIXME: Unwrap city
|
// FIXME: Unwrap city
|
||||||
|
|
|
||||||
BIN
status-bar.wasm
Executable file
BIN
status-bar.wasm
Executable file
Binary file not shown.
Loading…
Add table
Reference in a new issue