refactor(screen): more multiple-users groundwork (#788)
* refactor(screen): support multiple mirrored clients * style(fmt): make rustfmt happy * style(clippy): make clippy happy * whitespace * github, y u no update CI?! * is this a cache issue? * is it the checkout cache? * no cache at all? * Debug * fix gototab * decoment * gototab none in wasm_vm * gototab none in wasm_vm * the fun never ends * style(fmt): make rustfmt happy
This commit is contained in:
parent
4fa55dbfde
commit
a99354a155
7 changed files with 680 additions and 513 deletions
|
|
@ -338,7 +338,7 @@ pub fn start_server(mut os_input: Box<dyn ServerOsApi>, socket_path: PathBuf) {
|
|||
let mode = mode_info.mode;
|
||||
session_data
|
||||
.senders
|
||||
.send_to_screen(ScreenInstruction::ChangeMode(mode_info.clone()))
|
||||
.send_to_screen(ScreenInstruction::ChangeMode(mode_info.clone(), client_id))
|
||||
.unwrap();
|
||||
session_data
|
||||
.senders
|
||||
|
|
|
|||
|
|
@ -29,13 +29,19 @@ use zellij_utils::{
|
|||
|
||||
pub type VteBytes = Vec<u8>;
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum ClientOrTabIndex {
|
||||
ClientId(ClientId),
|
||||
TabIndex(usize),
|
||||
}
|
||||
|
||||
/// Instructions related to PTYs (pseudoterminals).
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) enum PtyInstruction {
|
||||
SpawnTerminal(Option<TerminalAction>),
|
||||
SpawnTerminalVertically(Option<TerminalAction>),
|
||||
SpawnTerminalHorizontally(Option<TerminalAction>),
|
||||
UpdateActivePane(Option<PaneId>),
|
||||
SpawnTerminal(Option<TerminalAction>, ClientOrTabIndex),
|
||||
SpawnTerminalVertically(Option<TerminalAction>, ClientId),
|
||||
SpawnTerminalHorizontally(Option<TerminalAction>, ClientId),
|
||||
UpdateActivePane(Option<PaneId>, ClientId),
|
||||
NewTab(Option<TerminalAction>, Option<TabLayout>, ClientId),
|
||||
ClosePane(PaneId),
|
||||
CloseTab(Vec<PaneId>),
|
||||
|
|
@ -45,10 +51,10 @@ pub(crate) enum PtyInstruction {
|
|||
impl From<&PtyInstruction> for PtyContext {
|
||||
fn from(pty_instruction: &PtyInstruction) -> Self {
|
||||
match *pty_instruction {
|
||||
PtyInstruction::SpawnTerminal(_) => PtyContext::SpawnTerminal,
|
||||
PtyInstruction::SpawnTerminalVertically(_) => PtyContext::SpawnTerminalVertically,
|
||||
PtyInstruction::SpawnTerminalHorizontally(_) => PtyContext::SpawnTerminalHorizontally,
|
||||
PtyInstruction::UpdateActivePane(_) => PtyContext::UpdateActivePane,
|
||||
PtyInstruction::SpawnTerminal(..) => PtyContext::SpawnTerminal,
|
||||
PtyInstruction::SpawnTerminalVertically(..) => PtyContext::SpawnTerminalVertically,
|
||||
PtyInstruction::SpawnTerminalHorizontally(..) => PtyContext::SpawnTerminalHorizontally,
|
||||
PtyInstruction::UpdateActivePane(..) => PtyContext::UpdateActivePane,
|
||||
PtyInstruction::ClosePane(_) => PtyContext::ClosePane,
|
||||
PtyInstruction::CloseTab(_) => PtyContext::CloseTab,
|
||||
PtyInstruction::NewTab(..) => PtyContext::NewTab,
|
||||
|
|
@ -58,7 +64,7 @@ impl From<&PtyInstruction> for PtyContext {
|
|||
}
|
||||
|
||||
pub(crate) struct Pty {
|
||||
pub active_pane: Option<PaneId>,
|
||||
pub active_panes: HashMap<ClientId, PaneId>,
|
||||
pub bus: Bus<PtyInstruction>,
|
||||
pub id_to_child_pid: HashMap<RawFd, ChildId>,
|
||||
debug_to_file: bool,
|
||||
|
|
@ -72,29 +78,40 @@ pub(crate) fn pty_thread_main(mut pty: Pty, layout: LayoutFromYaml) {
|
|||
let (event, mut err_ctx) = pty.bus.recv().expect("failed to receive event on channel");
|
||||
err_ctx.add_call(ContextType::Pty((&event).into()));
|
||||
match event {
|
||||
PtyInstruction::SpawnTerminal(terminal_action) => {
|
||||
let pid = pty.spawn_terminal(terminal_action);
|
||||
PtyInstruction::SpawnTerminal(terminal_action, client_or_tab_index) => {
|
||||
let pid = pty.spawn_terminal(terminal_action, client_or_tab_index);
|
||||
pty.bus
|
||||
.senders
|
||||
.send_to_screen(ScreenInstruction::NewPane(PaneId::Terminal(pid)))
|
||||
.send_to_screen(ScreenInstruction::NewPane(
|
||||
PaneId::Terminal(pid),
|
||||
client_or_tab_index,
|
||||
))
|
||||
.unwrap();
|
||||
}
|
||||
PtyInstruction::SpawnTerminalVertically(terminal_action) => {
|
||||
let pid = pty.spawn_terminal(terminal_action);
|
||||
PtyInstruction::SpawnTerminalVertically(terminal_action, client_id) => {
|
||||
let pid =
|
||||
pty.spawn_terminal(terminal_action, ClientOrTabIndex::ClientId(client_id));
|
||||
pty.bus
|
||||
.senders
|
||||
.send_to_screen(ScreenInstruction::VerticalSplit(PaneId::Terminal(pid)))
|
||||
.send_to_screen(ScreenInstruction::VerticalSplit(
|
||||
PaneId::Terminal(pid),
|
||||
client_id,
|
||||
))
|
||||
.unwrap();
|
||||
}
|
||||
PtyInstruction::SpawnTerminalHorizontally(terminal_action) => {
|
||||
let pid = pty.spawn_terminal(terminal_action);
|
||||
PtyInstruction::SpawnTerminalHorizontally(terminal_action, client_id) => {
|
||||
let pid =
|
||||
pty.spawn_terminal(terminal_action, ClientOrTabIndex::ClientId(client_id));
|
||||
pty.bus
|
||||
.senders
|
||||
.send_to_screen(ScreenInstruction::HorizontalSplit(PaneId::Terminal(pid)))
|
||||
.send_to_screen(ScreenInstruction::HorizontalSplit(
|
||||
PaneId::Terminal(pid),
|
||||
client_id,
|
||||
))
|
||||
.unwrap();
|
||||
}
|
||||
PtyInstruction::UpdateActivePane(pane_id) => {
|
||||
pty.set_active_pane(pane_id);
|
||||
PtyInstruction::UpdateActivePane(pane_id, client_id) => {
|
||||
pty.set_active_pane(pane_id, client_id);
|
||||
}
|
||||
PtyInstruction::NewTab(terminal_action, tab_layout, client_id) => {
|
||||
let tab_name = tab_layout.as_ref().and_then(|layout| {
|
||||
|
|
@ -115,11 +132,14 @@ pub(crate) fn pty_thread_main(mut pty: Pty, layout: LayoutFromYaml) {
|
|||
// clear current name at first
|
||||
pty.bus
|
||||
.senders
|
||||
.send_to_screen(ScreenInstruction::UpdateTabName(vec![0]))
|
||||
.send_to_screen(ScreenInstruction::UpdateTabName(vec![0], client_id))
|
||||
.unwrap();
|
||||
pty.bus
|
||||
.senders
|
||||
.send_to_screen(ScreenInstruction::UpdateTabName(tab_name.into_bytes()))
|
||||
.send_to_screen(ScreenInstruction::UpdateTabName(
|
||||
tab_name.into_bytes(),
|
||||
client_id,
|
||||
))
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
|
@ -229,10 +249,13 @@ fn stream_terminal_bytes(
|
|||
}
|
||||
async_send_to_screen(senders.clone(), ScreenInstruction::Render).await;
|
||||
|
||||
// this is a little hacky, and is because the tests end the file as soon as
|
||||
// we read everything, rather than hanging until there is new data
|
||||
// a better solution would be to fix the test fakes, but this will do for now
|
||||
async_send_to_screen(senders, ScreenInstruction::ClosePane(PaneId::Terminal(pid)))
|
||||
// we send ClosePane here so that the screen knows to close this tab if the process
|
||||
// inside the terminal exited on its own (eg. the user typed "exit<ENTER>" inside a
|
||||
// bash shell)
|
||||
async_send_to_screen(
|
||||
senders,
|
||||
ScreenInstruction::ClosePane(PaneId::Terminal(pid), None),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
})
|
||||
|
|
@ -241,29 +264,40 @@ fn stream_terminal_bytes(
|
|||
impl Pty {
|
||||
pub fn new(bus: Bus<PtyInstruction>, debug_to_file: bool) -> Self {
|
||||
Pty {
|
||||
active_pane: None,
|
||||
active_panes: HashMap::new(),
|
||||
bus,
|
||||
id_to_child_pid: HashMap::new(),
|
||||
debug_to_file,
|
||||
task_handles: HashMap::new(),
|
||||
}
|
||||
}
|
||||
pub fn get_default_terminal(&self) -> TerminalAction {
|
||||
pub fn get_default_terminal(&self, client_id: Option<ClientId>) -> TerminalAction {
|
||||
TerminalAction::RunCommand(RunCommand {
|
||||
args: vec![],
|
||||
command: PathBuf::from(env::var("SHELL").expect("Could not find the SHELL variable")),
|
||||
cwd: self
|
||||
.active_pane
|
||||
cwd: client_id
|
||||
.and_then(|client_id| self.active_panes.get(&client_id))
|
||||
.and_then(|pane| match pane {
|
||||
PaneId::Plugin(..) => None,
|
||||
PaneId::Terminal(id) => self.id_to_child_pid.get(&id).and_then(|id| id.shell),
|
||||
PaneId::Terminal(id) => self.id_to_child_pid.get(id).and_then(|id| id.shell),
|
||||
})
|
||||
.and_then(|id| self.bus.os_input.as_ref().map(|input| input.get_cwd(id)))
|
||||
.flatten(),
|
||||
})
|
||||
}
|
||||
pub fn spawn_terminal(&mut self, terminal_action: Option<TerminalAction>) -> RawFd {
|
||||
let terminal_action = terminal_action.unwrap_or_else(|| self.get_default_terminal());
|
||||
pub fn spawn_terminal(
|
||||
&mut self,
|
||||
terminal_action: Option<TerminalAction>,
|
||||
client_or_tab_index: ClientOrTabIndex,
|
||||
) -> RawFd {
|
||||
let terminal_action = match client_or_tab_index {
|
||||
ClientOrTabIndex::ClientId(client_id) => {
|
||||
terminal_action.unwrap_or_else(|| self.get_default_terminal(Some(client_id)))
|
||||
}
|
||||
ClientOrTabIndex::TabIndex(_) => {
|
||||
terminal_action.unwrap_or_else(|| self.get_default_terminal(None))
|
||||
}
|
||||
};
|
||||
let (pid_primary, child_id): (RawFd, ChildId) = self
|
||||
.bus
|
||||
.os_input
|
||||
|
|
@ -286,7 +320,8 @@ impl Pty {
|
|||
default_shell: Option<TerminalAction>,
|
||||
client_id: ClientId,
|
||||
) {
|
||||
let default_shell = default_shell.unwrap_or_else(|| self.get_default_terminal());
|
||||
let default_shell =
|
||||
default_shell.unwrap_or_else(|| self.get_default_terminal(Some(client_id)));
|
||||
let extracted_run_instructions = layout.extract_run_instructions();
|
||||
let mut new_pane_pids = vec![];
|
||||
for run_instruction in extracted_run_instructions {
|
||||
|
|
@ -368,8 +403,10 @@ impl Pty {
|
|||
self.close_pane(id);
|
||||
});
|
||||
}
|
||||
pub fn set_active_pane(&mut self, pane_id: Option<PaneId>) {
|
||||
self.active_pane = pane_id;
|
||||
pub fn set_active_pane(&mut self, pane_id: Option<PaneId>, client_id: ClientId) {
|
||||
if let Some(pane_id) = pane_id {
|
||||
self.active_panes.insert(client_id, pane_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,8 +3,11 @@ use std::sync::{Arc, RwLock};
|
|||
use zellij_utils::zellij_tile::data::Event;
|
||||
|
||||
use crate::{
|
||||
os_input_output::ServerOsApi, pty::PtyInstruction, screen::ScreenInstruction,
|
||||
wasm_vm::PluginInstruction, ServerInstruction, SessionMetaData, SessionState,
|
||||
os_input_output::ServerOsApi,
|
||||
pty::{ClientOrTabIndex, PtyInstruction},
|
||||
screen::ScreenInstruction,
|
||||
wasm_vm::PluginInstruction,
|
||||
ServerInstruction, SessionMetaData, SessionState,
|
||||
};
|
||||
use zellij_utils::{
|
||||
channels::SenderWithContext,
|
||||
|
|
@ -34,17 +37,17 @@ fn route_action(
|
|||
Action::ToggleTab => {
|
||||
session
|
||||
.senders
|
||||
.send_to_screen(ScreenInstruction::ToggleTab)
|
||||
.send_to_screen(ScreenInstruction::ToggleTab(client_id))
|
||||
.unwrap();
|
||||
}
|
||||
Action::Write(val) => {
|
||||
session
|
||||
.senders
|
||||
.send_to_screen(ScreenInstruction::ClearScroll)
|
||||
.send_to_screen(ScreenInstruction::ClearScroll(client_id))
|
||||
.unwrap();
|
||||
session
|
||||
.senders
|
||||
.send_to_screen(ScreenInstruction::WriteCharacter(val))
|
||||
.send_to_screen(ScreenInstruction::WriteCharacter(val, client_id))
|
||||
.unwrap();
|
||||
}
|
||||
Action::SwitchToMode(mode) => {
|
||||
|
|
@ -61,11 +64,10 @@ fn route_action(
|
|||
.unwrap();
|
||||
session
|
||||
.senders
|
||||
.send_to_screen(ScreenInstruction::ChangeMode(get_mode_info(
|
||||
mode,
|
||||
palette,
|
||||
session.capabilities,
|
||||
)))
|
||||
.send_to_screen(ScreenInstruction::ChangeMode(
|
||||
get_mode_info(mode, palette, session.capabilities),
|
||||
client_id,
|
||||
))
|
||||
.unwrap();
|
||||
session
|
||||
.senders
|
||||
|
|
@ -74,103 +76,103 @@ fn route_action(
|
|||
}
|
||||
Action::Resize(direction) => {
|
||||
let screen_instr = match direction {
|
||||
Direction::Left => ScreenInstruction::ResizeLeft,
|
||||
Direction::Right => ScreenInstruction::ResizeRight,
|
||||
Direction::Up => ScreenInstruction::ResizeUp,
|
||||
Direction::Down => ScreenInstruction::ResizeDown,
|
||||
Direction::Left => ScreenInstruction::ResizeLeft(client_id),
|
||||
Direction::Right => ScreenInstruction::ResizeRight(client_id),
|
||||
Direction::Up => ScreenInstruction::ResizeUp(client_id),
|
||||
Direction::Down => ScreenInstruction::ResizeDown(client_id),
|
||||
};
|
||||
session.senders.send_to_screen(screen_instr).unwrap();
|
||||
}
|
||||
Action::SwitchFocus => {
|
||||
session
|
||||
.senders
|
||||
.send_to_screen(ScreenInstruction::SwitchFocus)
|
||||
.send_to_screen(ScreenInstruction::SwitchFocus(client_id))
|
||||
.unwrap();
|
||||
}
|
||||
Action::FocusNextPane => {
|
||||
session
|
||||
.senders
|
||||
.send_to_screen(ScreenInstruction::FocusNextPane)
|
||||
.send_to_screen(ScreenInstruction::FocusNextPane(client_id))
|
||||
.unwrap();
|
||||
}
|
||||
Action::FocusPreviousPane => {
|
||||
session
|
||||
.senders
|
||||
.send_to_screen(ScreenInstruction::FocusPreviousPane)
|
||||
.send_to_screen(ScreenInstruction::FocusPreviousPane(client_id))
|
||||
.unwrap();
|
||||
}
|
||||
Action::MoveFocus(direction) => {
|
||||
let screen_instr = match direction {
|
||||
Direction::Left => ScreenInstruction::MoveFocusLeft,
|
||||
Direction::Right => ScreenInstruction::MoveFocusRight,
|
||||
Direction::Up => ScreenInstruction::MoveFocusUp,
|
||||
Direction::Down => ScreenInstruction::MoveFocusDown,
|
||||
Direction::Left => ScreenInstruction::MoveFocusLeft(client_id),
|
||||
Direction::Right => ScreenInstruction::MoveFocusRight(client_id),
|
||||
Direction::Up => ScreenInstruction::MoveFocusUp(client_id),
|
||||
Direction::Down => ScreenInstruction::MoveFocusDown(client_id),
|
||||
};
|
||||
session.senders.send_to_screen(screen_instr).unwrap();
|
||||
}
|
||||
Action::MoveFocusOrTab(direction) => {
|
||||
let screen_instr = match direction {
|
||||
Direction::Left => ScreenInstruction::MoveFocusLeftOrPreviousTab,
|
||||
Direction::Right => ScreenInstruction::MoveFocusRightOrNextTab,
|
||||
Direction::Left => ScreenInstruction::MoveFocusLeftOrPreviousTab(client_id),
|
||||
Direction::Right => ScreenInstruction::MoveFocusRightOrNextTab(client_id),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
session.senders.send_to_screen(screen_instr).unwrap();
|
||||
}
|
||||
Action::MovePane(direction) => {
|
||||
let screen_instr = match direction {
|
||||
Direction::Left => ScreenInstruction::MovePaneLeft,
|
||||
Direction::Right => ScreenInstruction::MovePaneRight,
|
||||
Direction::Up => ScreenInstruction::MovePaneUp,
|
||||
Direction::Down => ScreenInstruction::MovePaneDown,
|
||||
Direction::Left => ScreenInstruction::MovePaneLeft(client_id),
|
||||
Direction::Right => ScreenInstruction::MovePaneRight(client_id),
|
||||
Direction::Up => ScreenInstruction::MovePaneUp(client_id),
|
||||
Direction::Down => ScreenInstruction::MovePaneDown(client_id),
|
||||
};
|
||||
session.senders.send_to_screen(screen_instr).unwrap();
|
||||
}
|
||||
Action::ScrollUp => {
|
||||
session
|
||||
.senders
|
||||
.send_to_screen(ScreenInstruction::ScrollUp)
|
||||
.send_to_screen(ScreenInstruction::ScrollUp(client_id))
|
||||
.unwrap();
|
||||
}
|
||||
Action::ScrollUpAt(point) => {
|
||||
session
|
||||
.senders
|
||||
.send_to_screen(ScreenInstruction::ScrollUpAt(point))
|
||||
.send_to_screen(ScreenInstruction::ScrollUpAt(point, client_id))
|
||||
.unwrap();
|
||||
}
|
||||
Action::ScrollDown => {
|
||||
session
|
||||
.senders
|
||||
.send_to_screen(ScreenInstruction::ScrollDown)
|
||||
.send_to_screen(ScreenInstruction::ScrollDown(client_id))
|
||||
.unwrap();
|
||||
}
|
||||
Action::ScrollDownAt(point) => {
|
||||
session
|
||||
.senders
|
||||
.send_to_screen(ScreenInstruction::ScrollDownAt(point))
|
||||
.send_to_screen(ScreenInstruction::ScrollDownAt(point, client_id))
|
||||
.unwrap();
|
||||
}
|
||||
Action::ScrollToBottom => {
|
||||
session
|
||||
.senders
|
||||
.send_to_screen(ScreenInstruction::ScrollToBottom)
|
||||
.send_to_screen(ScreenInstruction::ScrollToBottom(client_id))
|
||||
.unwrap();
|
||||
}
|
||||
Action::PageScrollUp => {
|
||||
session
|
||||
.senders
|
||||
.send_to_screen(ScreenInstruction::PageScrollUp)
|
||||
.send_to_screen(ScreenInstruction::PageScrollUp(client_id))
|
||||
.unwrap();
|
||||
}
|
||||
Action::PageScrollDown => {
|
||||
session
|
||||
.senders
|
||||
.send_to_screen(ScreenInstruction::PageScrollDown)
|
||||
.send_to_screen(ScreenInstruction::PageScrollDown(client_id))
|
||||
.unwrap();
|
||||
}
|
||||
Action::ToggleFocusFullscreen => {
|
||||
session
|
||||
.senders
|
||||
.send_to_screen(ScreenInstruction::ToggleActiveTerminalFullscreen)
|
||||
.send_to_screen(ScreenInstruction::ToggleActiveTerminalFullscreen(client_id))
|
||||
.unwrap();
|
||||
}
|
||||
Action::TogglePaneFrames => {
|
||||
|
|
@ -182,31 +184,43 @@ fn route_action(
|
|||
Action::NewPane(direction) => {
|
||||
let shell = session.default_shell.clone();
|
||||
let pty_instr = match direction {
|
||||
Some(Direction::Left) => PtyInstruction::SpawnTerminalVertically(shell),
|
||||
Some(Direction::Right) => PtyInstruction::SpawnTerminalVertically(shell),
|
||||
Some(Direction::Up) => PtyInstruction::SpawnTerminalHorizontally(shell),
|
||||
Some(Direction::Down) => PtyInstruction::SpawnTerminalHorizontally(shell),
|
||||
Some(Direction::Left) => PtyInstruction::SpawnTerminalVertically(shell, client_id),
|
||||
Some(Direction::Right) => PtyInstruction::SpawnTerminalVertically(shell, client_id),
|
||||
Some(Direction::Up) => PtyInstruction::SpawnTerminalHorizontally(shell, client_id),
|
||||
Some(Direction::Down) => {
|
||||
PtyInstruction::SpawnTerminalHorizontally(shell, client_id)
|
||||
}
|
||||
// No direction specified - try to put it in the biggest available spot
|
||||
None => PtyInstruction::SpawnTerminal(shell),
|
||||
None => PtyInstruction::SpawnTerminal(shell, ClientOrTabIndex::ClientId(client_id)),
|
||||
};
|
||||
session.senders.send_to_pty(pty_instr).unwrap();
|
||||
}
|
||||
Action::Run(command) => {
|
||||
let run_cmd = Some(TerminalAction::RunCommand(command.clone().into()));
|
||||
let pty_instr = match command.direction {
|
||||
Some(Direction::Left) => PtyInstruction::SpawnTerminalVertically(run_cmd),
|
||||
Some(Direction::Right) => PtyInstruction::SpawnTerminalVertically(run_cmd),
|
||||
Some(Direction::Up) => PtyInstruction::SpawnTerminalHorizontally(run_cmd),
|
||||
Some(Direction::Down) => PtyInstruction::SpawnTerminalHorizontally(run_cmd),
|
||||
Some(Direction::Left) => {
|
||||
PtyInstruction::SpawnTerminalVertically(run_cmd, client_id)
|
||||
}
|
||||
Some(Direction::Right) => {
|
||||
PtyInstruction::SpawnTerminalVertically(run_cmd, client_id)
|
||||
}
|
||||
Some(Direction::Up) => {
|
||||
PtyInstruction::SpawnTerminalHorizontally(run_cmd, client_id)
|
||||
}
|
||||
Some(Direction::Down) => {
|
||||
PtyInstruction::SpawnTerminalHorizontally(run_cmd, client_id)
|
||||
}
|
||||
// No direction specified - try to put it in the biggest available spot
|
||||
None => PtyInstruction::SpawnTerminal(run_cmd),
|
||||
None => {
|
||||
PtyInstruction::SpawnTerminal(run_cmd, ClientOrTabIndex::ClientId(client_id))
|
||||
}
|
||||
};
|
||||
session.senders.send_to_pty(pty_instr).unwrap();
|
||||
}
|
||||
Action::CloseFocus => {
|
||||
session
|
||||
.senders
|
||||
.send_to_screen(ScreenInstruction::CloseFocusedPane)
|
||||
.send_to_screen(ScreenInstruction::CloseFocusedPane(client_id))
|
||||
.unwrap();
|
||||
}
|
||||
Action::NewTab(tab_layout) => {
|
||||
|
|
@ -219,37 +233,37 @@ fn route_action(
|
|||
Action::GoToNextTab => {
|
||||
session
|
||||
.senders
|
||||
.send_to_screen(ScreenInstruction::SwitchTabNext)
|
||||
.send_to_screen(ScreenInstruction::SwitchTabNext(client_id))
|
||||
.unwrap();
|
||||
}
|
||||
Action::GoToPreviousTab => {
|
||||
session
|
||||
.senders
|
||||
.send_to_screen(ScreenInstruction::SwitchTabPrev)
|
||||
.send_to_screen(ScreenInstruction::SwitchTabPrev(client_id))
|
||||
.unwrap();
|
||||
}
|
||||
Action::ToggleActiveSyncTab => {
|
||||
session
|
||||
.senders
|
||||
.send_to_screen(ScreenInstruction::ToggleActiveSyncTab)
|
||||
.send_to_screen(ScreenInstruction::ToggleActiveSyncTab(client_id))
|
||||
.unwrap();
|
||||
}
|
||||
Action::CloseTab => {
|
||||
session
|
||||
.senders
|
||||
.send_to_screen(ScreenInstruction::CloseTab)
|
||||
.send_to_screen(ScreenInstruction::CloseTab(client_id))
|
||||
.unwrap();
|
||||
}
|
||||
Action::GoToTab(i) => {
|
||||
session
|
||||
.senders
|
||||
.send_to_screen(ScreenInstruction::GoToTab(i))
|
||||
.send_to_screen(ScreenInstruction::GoToTab(i, Some(client_id)))
|
||||
.unwrap();
|
||||
}
|
||||
Action::TabNameInput(c) => {
|
||||
session
|
||||
.senders
|
||||
.send_to_screen(ScreenInstruction::UpdateTabName(c))
|
||||
.send_to_screen(ScreenInstruction::UpdateTabName(c, client_id))
|
||||
.unwrap();
|
||||
}
|
||||
Action::Quit => {
|
||||
|
|
@ -267,25 +281,25 @@ fn route_action(
|
|||
Action::LeftClick(point) => {
|
||||
session
|
||||
.senders
|
||||
.send_to_screen(ScreenInstruction::LeftClick(point))
|
||||
.send_to_screen(ScreenInstruction::LeftClick(point, client_id))
|
||||
.unwrap();
|
||||
}
|
||||
Action::MouseRelease(point) => {
|
||||
session
|
||||
.senders
|
||||
.send_to_screen(ScreenInstruction::MouseRelease(point))
|
||||
.send_to_screen(ScreenInstruction::MouseRelease(point, client_id))
|
||||
.unwrap();
|
||||
}
|
||||
Action::MouseHold(point) => {
|
||||
session
|
||||
.senders
|
||||
.send_to_screen(ScreenInstruction::MouseHold(point))
|
||||
.send_to_screen(ScreenInstruction::MouseHold(point, client_id))
|
||||
.unwrap();
|
||||
}
|
||||
Action::Copy => {
|
||||
session
|
||||
.senders
|
||||
.send_to_screen(ScreenInstruction::Copy)
|
||||
.send_to_screen(ScreenInstruction::Copy(client_id))
|
||||
.unwrap();
|
||||
}
|
||||
Action::NoOp => {}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -95,19 +95,16 @@ fn pane_content_offset(position_and_size: &PaneGeom, viewport: &Viewport) -> (us
|
|||
(columns_offset, rows_offset)
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct Output {
|
||||
pub client_render_instructions: HashMap<ClientId, String>,
|
||||
}
|
||||
|
||||
impl Output {
|
||||
pub fn new(client_ids: &HashSet<ClientId>) -> Self {
|
||||
let mut client_render_instructions = HashMap::new();
|
||||
pub fn add_clients(&mut self, client_ids: &HashSet<ClientId>) {
|
||||
for client_id in client_ids {
|
||||
client_render_instructions.insert(*client_id, String::new());
|
||||
}
|
||||
Output {
|
||||
client_render_instructions,
|
||||
self.client_render_instructions
|
||||
.insert(*client_id, String::new());
|
||||
}
|
||||
}
|
||||
pub fn push_str_to_all_clients(&mut self, to_push: &str) {
|
||||
|
|
@ -134,7 +131,6 @@ pub(crate) struct Tab {
|
|||
should_clear_display_before_rendering: bool,
|
||||
pub mode_info: ModeInfo,
|
||||
pub colors: Palette,
|
||||
pub is_active: bool,
|
||||
connected_clients: HashSet<ClientId>,
|
||||
draw_pane_frames: bool,
|
||||
pending_vte_events: HashMap<RawFd, Vec<VteBytes>>,
|
||||
|
|
@ -335,7 +331,6 @@ impl Tab {
|
|||
colors,
|
||||
draw_pane_frames,
|
||||
pending_vte_events: HashMap::new(),
|
||||
is_active: true,
|
||||
connected_clients,
|
||||
}
|
||||
}
|
||||
|
|
@ -800,14 +795,20 @@ impl Tab {
|
|||
resize_pty!(pane, self.os_api);
|
||||
}
|
||||
}
|
||||
pub fn render(&mut self) -> Option<Output> {
|
||||
pub fn render(&mut self, output: &mut Output) {
|
||||
if self.connected_clients.is_empty() || self.active_terminal.is_none() {
|
||||
return None;
|
||||
return;
|
||||
}
|
||||
for connected_client in self.connected_clients.iter() {
|
||||
// TODO: move this out of the render function
|
||||
self.senders
|
||||
.send_to_pty(PtyInstruction::UpdateActivePane(self.active_terminal))
|
||||
.send_to_pty(PtyInstruction::UpdateActivePane(
|
||||
self.active_terminal,
|
||||
*connected_client,
|
||||
))
|
||||
.unwrap();
|
||||
let mut output = Output::new(&self.connected_clients);
|
||||
}
|
||||
output.add_clients(&self.connected_clients);
|
||||
let mut boundaries = Boundaries::new(self.viewport);
|
||||
let hide_cursor = "\u{1b}[?25l";
|
||||
output.push_str_to_all_clients(hide_cursor);
|
||||
|
|
@ -878,7 +879,6 @@ impl Tab {
|
|||
output.push_str_to_all_clients(hide_cursor);
|
||||
}
|
||||
}
|
||||
Some(output)
|
||||
}
|
||||
fn get_panes(&self) -> impl Iterator<Item = (&PaneId, &Box<dyn Pane>)> {
|
||||
self.panes.iter()
|
||||
|
|
@ -2409,6 +2409,7 @@ impl Tab {
|
|||
// if we reached here, this is either the last pane or there's some sort of
|
||||
// configuration error (eg. we're trying to close a pane surrounded by fixed panes)
|
||||
self.panes.remove(&id);
|
||||
self.active_terminal = None;
|
||||
self.resize_whole_tab(self.display_area);
|
||||
}
|
||||
}
|
||||
|
|
@ -2587,7 +2588,8 @@ impl Tab {
|
|||
}
|
||||
|
||||
fn write_selection_to_clipboard(&self, selection: &str) {
|
||||
let mut output = Output::new(&self.connected_clients);
|
||||
let mut output = Output::default();
|
||||
output.add_clients(&self.connected_clients);
|
||||
output.push_str_to_all_clients(&format!(
|
||||
"\u{1b}]52;c;{}\u{1b}\\",
|
||||
base64::encode(selection)
|
||||
|
|
|
|||
|
|
@ -116,7 +116,7 @@ fn open_new_tab() {
|
|||
|
||||
assert_eq!(screen.tabs.len(), 2, "Screen now has two tabs");
|
||||
assert_eq!(
|
||||
screen.get_active_tab().unwrap().position,
|
||||
screen.get_active_tab(1).unwrap().position,
|
||||
1,
|
||||
"Active tab switched to new tab"
|
||||
);
|
||||
|
|
@ -132,10 +132,10 @@ pub fn switch_to_prev_tab() {
|
|||
|
||||
new_tab(&mut screen, 1);
|
||||
new_tab(&mut screen, 2);
|
||||
screen.switch_tab_prev();
|
||||
screen.switch_tab_prev(1);
|
||||
|
||||
assert_eq!(
|
||||
screen.get_active_tab().unwrap().position,
|
||||
screen.get_active_tab(1).unwrap().position,
|
||||
0,
|
||||
"Active tab switched to previous tab"
|
||||
);
|
||||
|
|
@ -151,11 +151,11 @@ pub fn switch_to_next_tab() {
|
|||
|
||||
new_tab(&mut screen, 1);
|
||||
new_tab(&mut screen, 2);
|
||||
screen.switch_tab_prev();
|
||||
screen.switch_tab_next();
|
||||
screen.switch_tab_prev(1);
|
||||
screen.switch_tab_next(1);
|
||||
|
||||
assert_eq!(
|
||||
screen.get_active_tab().unwrap().position,
|
||||
screen.get_active_tab(1).unwrap().position,
|
||||
1,
|
||||
"Active tab switched to next tab"
|
||||
);
|
||||
|
|
@ -171,11 +171,11 @@ pub fn close_tab() {
|
|||
|
||||
new_tab(&mut screen, 1);
|
||||
new_tab(&mut screen, 2);
|
||||
screen.close_tab();
|
||||
screen.close_tab(1);
|
||||
|
||||
assert_eq!(screen.tabs.len(), 1, "Only one tab left");
|
||||
assert_eq!(
|
||||
screen.get_active_tab().unwrap().position,
|
||||
screen.get_active_tab(1).unwrap().position,
|
||||
0,
|
||||
"Active tab switched to previous tab"
|
||||
);
|
||||
|
|
@ -197,13 +197,13 @@ pub fn close_the_middle_tab() {
|
|||
.values()
|
||||
.map(|t| (t.index, t.position, t.name.clone(), t.get_pane_ids()))
|
||||
.collect::<Vec<_>>());
|
||||
screen.switch_tab_prev();
|
||||
screen.switch_tab_prev(1);
|
||||
dbg!(screen
|
||||
.tabs
|
||||
.values()
|
||||
.map(|t| (t.index, t.position, t.name.clone(), t.get_pane_ids()))
|
||||
.collect::<Vec<_>>());
|
||||
screen.close_tab();
|
||||
screen.close_tab(1);
|
||||
dbg!(screen
|
||||
.tabs
|
||||
.values()
|
||||
|
|
@ -212,7 +212,7 @@ pub fn close_the_middle_tab() {
|
|||
|
||||
assert_eq!(screen.tabs.len(), 2, "Two tabs left");
|
||||
assert_eq!(
|
||||
screen.get_active_tab().unwrap().position,
|
||||
screen.get_active_tab(1).unwrap().position,
|
||||
1,
|
||||
"Active tab switched to previous tab"
|
||||
);
|
||||
|
|
@ -229,11 +229,11 @@ fn move_focus_left_at_left_screen_edge_changes_tab() {
|
|||
new_tab(&mut screen, 1);
|
||||
new_tab(&mut screen, 2);
|
||||
new_tab(&mut screen, 3);
|
||||
screen.switch_tab_prev();
|
||||
screen.move_focus_left_or_previous_tab();
|
||||
screen.switch_tab_prev(1);
|
||||
screen.move_focus_left_or_previous_tab(1);
|
||||
|
||||
assert_eq!(
|
||||
screen.get_active_tab().unwrap().position,
|
||||
screen.get_active_tab(1).unwrap().position,
|
||||
0,
|
||||
"Active tab switched to previous"
|
||||
);
|
||||
|
|
@ -250,11 +250,11 @@ fn move_focus_right_at_right_screen_edge_changes_tab() {
|
|||
new_tab(&mut screen, 1);
|
||||
new_tab(&mut screen, 2);
|
||||
new_tab(&mut screen, 3);
|
||||
screen.switch_tab_prev();
|
||||
screen.move_focus_right_or_next_tab();
|
||||
screen.switch_tab_prev(1);
|
||||
screen.move_focus_right_or_next_tab(1);
|
||||
|
||||
assert_eq!(
|
||||
screen.get_active_tab().unwrap().position,
|
||||
screen.get_active_tab(1).unwrap().position,
|
||||
2,
|
||||
"Active tab switched to next"
|
||||
);
|
||||
|
|
@ -270,19 +270,19 @@ pub fn toggle_to_previous_tab_simple() {
|
|||
|
||||
new_tab(&mut screen, 1);
|
||||
new_tab(&mut screen, 2);
|
||||
screen.go_to_tab(1);
|
||||
screen.go_to_tab(2);
|
||||
screen.go_to_tab(1, 1);
|
||||
screen.go_to_tab(2, 1);
|
||||
|
||||
screen.toggle_tab();
|
||||
screen.toggle_tab(1);
|
||||
assert_eq!(
|
||||
screen.get_active_tab().unwrap().position,
|
||||
screen.get_active_tab(1).unwrap().position,
|
||||
0,
|
||||
"Active tab toggler to previous tab"
|
||||
);
|
||||
|
||||
screen.toggle_tab();
|
||||
screen.toggle_tab(1);
|
||||
assert_eq!(
|
||||
screen.get_active_tab().unwrap().position,
|
||||
screen.get_active_tab(1).unwrap().position,
|
||||
1,
|
||||
"Active tab toggler to previous tab"
|
||||
);
|
||||
|
|
@ -301,38 +301,38 @@ pub fn toggle_to_previous_tab_create_tabs_only() {
|
|||
new_tab(&mut screen, 3);
|
||||
|
||||
assert_eq!(
|
||||
screen.tab_history,
|
||||
vec![None, Some(0), Some(1)],
|
||||
screen.tab_history.get(&1).unwrap(),
|
||||
&vec![0, 1],
|
||||
"Tab history is invalid"
|
||||
);
|
||||
|
||||
screen.toggle_tab();
|
||||
screen.toggle_tab(1);
|
||||
assert_eq!(
|
||||
screen.get_active_tab().unwrap().position,
|
||||
screen.get_active_tab(1).unwrap().position,
|
||||
1,
|
||||
"Active tab toggler to previous tab"
|
||||
);
|
||||
assert_eq!(
|
||||
screen.tab_history,
|
||||
vec![None, Some(0), Some(2)],
|
||||
screen.tab_history.get(&1).unwrap(),
|
||||
&vec![0, 2],
|
||||
"Tab history is invalid"
|
||||
);
|
||||
|
||||
screen.toggle_tab();
|
||||
screen.toggle_tab(1);
|
||||
assert_eq!(
|
||||
screen.get_active_tab().unwrap().position,
|
||||
screen.get_active_tab(1).unwrap().position,
|
||||
2,
|
||||
"Active tab toggler to previous tab"
|
||||
);
|
||||
assert_eq!(
|
||||
screen.tab_history,
|
||||
vec![None, Some(0), Some(1)],
|
||||
screen.tab_history.get(&1).unwrap(),
|
||||
&vec![0, 1],
|
||||
"Tab history is invalid"
|
||||
);
|
||||
|
||||
screen.toggle_tab();
|
||||
screen.toggle_tab(1);
|
||||
assert_eq!(
|
||||
screen.get_active_tab().unwrap().position,
|
||||
screen.get_active_tab(1).unwrap().position,
|
||||
1,
|
||||
"Active tab toggler to previous tab"
|
||||
);
|
||||
|
|
@ -352,84 +352,84 @@ pub fn toggle_to_previous_tab_delete() {
|
|||
new_tab(&mut screen, 4); // 3
|
||||
|
||||
assert_eq!(
|
||||
screen.tab_history,
|
||||
vec![None, Some(0), Some(1), Some(2)],
|
||||
screen.tab_history.get(&1).unwrap(),
|
||||
&vec![0, 1, 2],
|
||||
"Tab history is invalid"
|
||||
);
|
||||
assert_eq!(
|
||||
screen.get_active_tab().unwrap().position,
|
||||
screen.get_active_tab(1).unwrap().position,
|
||||
3,
|
||||
"Active tab toggler to previous tab"
|
||||
);
|
||||
|
||||
screen.toggle_tab();
|
||||
screen.toggle_tab(1);
|
||||
assert_eq!(
|
||||
screen.tab_history,
|
||||
vec![None, Some(0), Some(1), Some(3)],
|
||||
screen.tab_history.get(&1).unwrap(),
|
||||
&vec![0, 1, 3],
|
||||
"Tab history is invalid"
|
||||
);
|
||||
assert_eq!(
|
||||
screen.get_active_tab().unwrap().position,
|
||||
screen.get_active_tab(1).unwrap().position,
|
||||
2,
|
||||
"Active tab toggler to previous tab"
|
||||
);
|
||||
|
||||
screen.toggle_tab();
|
||||
screen.toggle_tab(1);
|
||||
assert_eq!(
|
||||
screen.tab_history,
|
||||
vec![None, Some(0), Some(1), Some(2)],
|
||||
screen.tab_history.get(&1).unwrap(),
|
||||
&vec![0, 1, 2],
|
||||
"Tab history is invalid"
|
||||
);
|
||||
assert_eq!(
|
||||
screen.get_active_tab().unwrap().position,
|
||||
screen.get_active_tab(1).unwrap().position,
|
||||
3,
|
||||
"Active tab toggler to previous tab"
|
||||
);
|
||||
|
||||
screen.switch_tab_prev();
|
||||
screen.switch_tab_prev(1);
|
||||
assert_eq!(
|
||||
screen.tab_history,
|
||||
vec![None, Some(0), Some(1), Some(3)],
|
||||
screen.tab_history.get(&1).unwrap(),
|
||||
&vec![0, 1, 3],
|
||||
"Tab history is invalid"
|
||||
);
|
||||
assert_eq!(
|
||||
screen.get_active_tab().unwrap().position,
|
||||
screen.get_active_tab(1).unwrap().position,
|
||||
2,
|
||||
"Active tab toggler to previous tab"
|
||||
);
|
||||
screen.switch_tab_prev();
|
||||
screen.switch_tab_prev(1);
|
||||
assert_eq!(
|
||||
screen.tab_history,
|
||||
vec![None, Some(0), Some(3), Some(2)],
|
||||
screen.tab_history.get(&1).unwrap(),
|
||||
&vec![0, 3, 2],
|
||||
"Tab history is invalid"
|
||||
);
|
||||
assert_eq!(
|
||||
screen.get_active_tab().unwrap().position,
|
||||
screen.get_active_tab(1).unwrap().position,
|
||||
1,
|
||||
"Active tab toggler to previous tab"
|
||||
);
|
||||
|
||||
screen.close_tab();
|
||||
screen.close_tab(1);
|
||||
assert_eq!(
|
||||
screen.tab_history,
|
||||
vec![None, Some(0), Some(3)],
|
||||
screen.tab_history.get(&1).unwrap(),
|
||||
&vec![0, 3],
|
||||
"Tab history is invalid"
|
||||
);
|
||||
assert_eq!(
|
||||
screen.get_active_tab().unwrap().position,
|
||||
screen.get_active_tab(1).unwrap().position,
|
||||
1,
|
||||
"Active tab toggler to previous tab"
|
||||
);
|
||||
|
||||
screen.toggle_tab();
|
||||
screen.toggle_tab(1);
|
||||
assert_eq!(
|
||||
screen.get_active_tab().unwrap().position,
|
||||
screen.get_active_tab(1).unwrap().position,
|
||||
2,
|
||||
"Active tab toggler to previous tab"
|
||||
);
|
||||
assert_eq!(
|
||||
screen.tab_history,
|
||||
vec![None, Some(0), Some(2)],
|
||||
screen.tab_history.get(&1).unwrap(),
|
||||
&vec![0, 2],
|
||||
"Tab history is invalid"
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ use zellij_tile::data::{Event, EventType, PluginIds};
|
|||
use crate::{
|
||||
logging_pipe::LoggingPipe,
|
||||
panes::PaneId,
|
||||
pty::PtyInstruction,
|
||||
pty::{ClientOrTabIndex, PtyInstruction},
|
||||
screen::ScreenInstruction,
|
||||
thread_bus::{Bus, ThreadSenders},
|
||||
};
|
||||
|
|
@ -60,6 +60,7 @@ pub(crate) struct PluginEnv {
|
|||
pub senders: ThreadSenders,
|
||||
pub wasi_env: WasiEnv,
|
||||
pub subscriptions: Arc<Mutex<HashSet<EventType>>>,
|
||||
pub tab_index: usize,
|
||||
plugin_own_data_dir: PathBuf,
|
||||
}
|
||||
|
||||
|
|
@ -208,6 +209,7 @@ fn start_plugin(
|
|||
wasi_env,
|
||||
subscriptions: Arc::new(Mutex::new(HashSet::new())),
|
||||
plugin_own_data_dir,
|
||||
tab_index,
|
||||
};
|
||||
|
||||
let zellij = zellij_exports(store, &plugin_env);
|
||||
|
|
@ -293,16 +295,17 @@ fn host_open_file(plugin_env: &PluginEnv) {
|
|||
let path: PathBuf = wasi_read_object(&plugin_env.wasi_env);
|
||||
plugin_env
|
||||
.senders
|
||||
.send_to_pty(PtyInstruction::SpawnTerminal(Some(
|
||||
TerminalAction::OpenFile(path),
|
||||
)))
|
||||
.send_to_pty(PtyInstruction::SpawnTerminal(
|
||||
Some(TerminalAction::OpenFile(path)),
|
||||
ClientOrTabIndex::TabIndex(plugin_env.tab_index),
|
||||
))
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
fn host_switch_tab_to(plugin_env: &PluginEnv, tab_idx: u32) {
|
||||
plugin_env
|
||||
.senders
|
||||
.send_to_screen(ScreenInstruction::GoToTab(tab_idx))
|
||||
.send_to_screen(ScreenInstruction::GoToTab(tab_idx, None)) // this is a hack, we should be able to return the client id here
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue