diff options
author | Aram Drevekenin <aram@poor.dev> | 2021-12-08 18:41:41 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-12-08 18:41:41 +0100 |
commit | 92bddf1b798ec4ecfce8831552e1f218273bb50f (patch) | |
tree | aa65624c877a1407ede75a414461559e0cbb530f | |
parent | 56e85f87d6c365816cca71c496aa7e49709e0b11 (diff) |
fix(plugins): support multiple users (#930)
* fix(plugins): support multiple clients
* fix(style): make clippy happy
-rw-r--r-- | src/tests/e2e/snapshots/zellij__tests__e2e__cases__mirrored_sessions.snap | 2 | ||||
-rw-r--r-- | zellij-server/src/lib.rs | 41 | ||||
-rw-r--r-- | zellij-server/src/panes/plugin_pane.rs | 12 | ||||
-rw-r--r-- | zellij-server/src/panes/terminal_pane.rs | 3 | ||||
-rw-r--r-- | zellij-server/src/route.rs | 15 | ||||
-rw-r--r-- | zellij-server/src/screen.rs | 49 | ||||
-rw-r--r-- | zellij-server/src/tab.rs | 117 | ||||
-rw-r--r-- | zellij-server/src/ui/pane_contents_and_ui.rs | 31 | ||||
-rw-r--r-- | zellij-server/src/unit/tab_tests.rs | 1 | ||||
-rw-r--r-- | zellij-server/src/wasm_vm.rs | 123 | ||||
-rw-r--r-- | zellij-utils/src/errors.rs | 2 |
11 files changed, 305 insertions, 91 deletions
diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__mirrored_sessions.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__mirrored_sessions.snap index 7dbfbbd1e..bfb3fcd9d 100644 --- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__mirrored_sessions.snap +++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__mirrored_sessions.snap @@ -26,4 +26,4 @@ expression: first_runner_snapshot │ ││ │ └──────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘ Ctrl + <g> LOCK <p> PANE <t> TAB <n> RESIZE <h> MOVE <s> SCROLL <o> SESSION <q> QUIT - <←↓↑→> Move focus / <n> New / <x> Close / <r> Rename / <s> Sync / <Tab> Toggle / <ENTER> Select pane + Tip: Alt + <n> => new pane. Alt + <[] or hjkl> => navigate. Alt + <+-> => resize pane. diff --git a/zellij-server/src/lib.rs b/zellij-server/src/lib.rs index 391a03f6f..6a94742ca 100644 --- a/zellij-server/src/lib.rs +++ b/zellij-server/src/lib.rs @@ -318,6 +318,14 @@ pub fn start_server(mut os_input: Box<dyn ServerOsApi>, socket_path: PathBuf) { } else { spawn_tabs(None); } + session_data + .read() + .unwrap() + .as_ref() + .unwrap() + .senders + .send_to_plugin(PluginInstruction::AddClient(client_id)) + .unwrap(); } ServerInstruction::AttachClient(attrs, options, client_id) => { let rlock = session_data.read().unwrap(); @@ -339,6 +347,10 @@ pub fn start_server(mut os_input: Box<dyn ServerOsApi>, socket_path: PathBuf) { .senders .send_to_screen(ScreenInstruction::AddClient(client_id)) .unwrap(); + session_data + .senders + .send_to_plugin(PluginInstruction::AddClient(client_id)) + .unwrap(); let default_mode = options.default_mode.unwrap_or_default(); let mode_info = get_mode_info(default_mode, attrs.palette, session_data.capabilities); @@ -351,12 +363,11 @@ pub fn start_server(mut os_input: Box<dyn ServerOsApi>, socket_path: PathBuf) { .senders .send_to_plugin(PluginInstruction::Update( None, + Some(client_id), Event::ModeUpdate(mode_info), )) .unwrap(); - for client_id in session_state.read().unwrap().clients.keys() { - os_input.send_to_client(*client_id, ServerToClientMsg::SwitchToMode(mode)); - } + os_input.send_to_client(client_id, ServerToClientMsg::SwitchToMode(mode)); } ServerInstruction::UnblockInputThread => { for client_id in session_state.read().unwrap().clients.keys() { @@ -385,6 +396,14 @@ pub fn start_server(mut os_input: Box<dyn ServerOsApi>, socket_path: PathBuf) { .senders .send_to_screen(ScreenInstruction::RemoveClient(client_id)) .unwrap(); + session_data + .write() + .unwrap() + .as_ref() + .unwrap() + .senders + .send_to_plugin(PluginInstruction::RemoveClient(client_id)) + .unwrap(); } if session_state.read().unwrap().clients.is_empty() { *session_data.write().unwrap() = None; @@ -412,6 +431,14 @@ pub fn start_server(mut os_input: Box<dyn ServerOsApi>, socket_path: PathBuf) { .senders .send_to_screen(ScreenInstruction::RemoveClient(client_id)) .unwrap(); + session_data + .write() + .unwrap() + .as_ref() + .unwrap() + .senders + .send_to_plugin(PluginInstruction::RemoveClient(client_id)) + .unwrap(); } } ServerInstruction::KillSession => { @@ -444,6 +471,14 @@ pub fn start_server(mut os_input: Box<dyn ServerOsApi>, socket_path: PathBuf) { .senders .send_to_screen(ScreenInstruction::RemoveClient(client_id)) .unwrap(); + session_data + .write() + .unwrap() + .as_ref() + .unwrap() + .senders + .send_to_plugin(PluginInstruction::RemoveClient(client_id)) + .unwrap(); } } ServerInstruction::Render(mut output) => { diff --git a/zellij-server/src/panes/plugin_pane.rs b/zellij-server/src/panes/plugin_pane.rs index e34145f45..a9d5d3e5c 100644 --- a/zellij-server/src/panes/plugin_pane.rs +++ b/zellij-server/src/panes/plugin_pane.rs @@ -131,7 +131,10 @@ impl Pane for PluginPane { fn set_selectable(&mut self, selectable: bool) { self.selectable = selectable; } - fn render(&mut self) -> Option<String> { + fn render(&mut self, client_id: Option<ClientId>) -> Option<String> { + // this is a bit of a hack but works in a pinch + client_id?; + let client_id = client_id.unwrap(); // if self.should_render { if true { // while checking should_render rather than rendering each pane every time @@ -145,6 +148,7 @@ impl Pane for PluginPane { .send(PluginInstruction::Render( buf_tx, self.pid, + client_id, self.get_content_rows(), self.get_content_columns(), )) @@ -274,6 +278,7 @@ impl Pane for PluginPane { self.send_plugin_instructions .send(PluginInstruction::Update( Some(self.pid), + None, Event::Mouse(Mouse::ScrollUp(count)), )) .unwrap(); @@ -282,6 +287,7 @@ impl Pane for PluginPane { self.send_plugin_instructions .send(PluginInstruction::Update( Some(self.pid), + None, Event::Mouse(Mouse::ScrollDown(count)), )) .unwrap(); @@ -293,6 +299,7 @@ impl Pane for PluginPane { self.send_plugin_instructions .send(PluginInstruction::Update( Some(self.pid), + None, Event::Mouse(Mouse::LeftClick(start.line.0, start.column.0)), )) .unwrap(); @@ -301,6 +308,7 @@ impl Pane for PluginPane { self.send_plugin_instructions .send(PluginInstruction::Update( Some(self.pid), + None, Event::Mouse(Mouse::Hold(position.line.0, position.column.0)), )) .unwrap(); @@ -309,6 +317,7 @@ impl Pane for PluginPane { self.send_plugin_instructions .send(PluginInstruction::Update( Some(self.pid), + None, Event::Mouse(Mouse::Release( end.map(|Position { line, column }| (line.0, column.0)), )), @@ -342,6 +351,7 @@ impl Pane for PluginPane { self.send_plugin_instructions .send(PluginInstruction::Update( Some(self.pid), + None, Event::Mouse(Mouse::RightClick(to.line.0, to.column.0)), )) .unwrap(); diff --git a/zellij-server/src/panes/terminal_pane.rs b/zellij-server/src/panes/terminal_pane.rs index f2e340324..a6da76709 100644 --- a/zellij-server/src/panes/terminal_pane.rs +++ b/zellij-server/src/panes/terminal_pane.rs @@ -183,7 +183,8 @@ impl Pane for TerminalPane { fn set_selectable(&mut self, selectable: bool) { self.selectable = selectable; } - fn render(&mut self) -> Option<String> { + fn render(&mut self, _client_id: Option<ClientId>) -> Option<String> { + // we don't use client_id because terminal panes render the same for all users if self.should_render() { let mut vte_output = String::new(); let mut character_styles = CharacterStyles::new(); diff --git a/zellij-server/src/route.rs b/zellij-server/src/route.rs index 8bb82c124..e24d2d4f1 100644 --- a/zellij-server/src/route.rs +++ b/zellij-server/src/route.rs @@ -31,7 +31,11 @@ fn route_action( let mut should_break = false; session .senders - .send_to_plugin(PluginInstruction::Update(None, Event::InputReceived)) + .send_to_plugin(PluginInstruction::Update( + None, + Some(client_id), + Event::InputReceived, + )) .unwrap(); match action { Action::ToggleTab => { @@ -70,6 +74,7 @@ fn route_action( .senders .send_to_plugin(PluginInstruction::Update( None, + Some(client_id), Event::ModeUpdate(get_mode_info(mode, palette, session.capabilities)), )) .unwrap(); @@ -379,12 +384,8 @@ pub(crate) fn route_thread_main( ClientToServerMsg::Action(action) => { if let Some(rlocked_sessions) = rlocked_sessions.as_ref() { if let Action::SwitchToMode(input_mode) = action { - for client_id in session_state.read().unwrap().clients.keys() { - os_input.send_to_client( - *client_id, - ServerToClientMsg::SwitchToMode(input_mode), - ); - } + os_input + .send_to_client(client_id, ServerToClientMsg::SwitchToMode(input_mode)); } if route_action(action, rlocked_sessions, &*os_input, &to_server, client_id) { break; diff --git a/zellij-server/src/screen.rs b/zellij-server/src/screen.rs index 48b577925..1400235ea 100644 --- a/zellij-server/src/screen.rs +++ b/zellij-server/src/screen.rs @@ -183,7 +183,8 @@ pub(crate) struct Screen { /// The indices of this [`Screen`]'s active [`Tab`]s. active_tab_indices: BTreeMap<ClientId, usize>, tab_history: BTreeMap<ClientId, Vec<usize>>, - mode_info: ModeInfo, + mode_info: BTreeMap<ClientId, ModeInfo>, + default_mode_info: ModeInfo, // TODO: restructure ModeInfo to prevent this duplication colors: Palette, draw_pane_frames: bool, } @@ -206,7 +207,8 @@ impl Screen { tabs: BTreeMap::new(), overlay: OverlayWindow::default(), tab_history: BTreeMap::new(), - mode_info, + mode_info: BTreeMap::new(), + default_mode_info: mode_info, draw_pane_frames, } } @@ -241,12 +243,15 @@ impl Screen { } } fn move_clients(&mut self, source_index: usize, destination_index: usize) { - let connected_clients_in_source_tab = { + let (connected_clients_in_source_tab, client_mode_infos_in_source_tab) = { let source_tab = self.tabs.get_mut(&source_index).unwrap(); source_tab.drain_connected_clients() }; let destination_tab = self.tabs.get_mut(&destination_index).unwrap(); - destination_tab.add_multiple_clients(&connected_clients_in_source_tab); + destination_tab.add_multiple_clients( + connected_clients_in_source_tab, + client_mode_infos_in_source_tab, + ); } /// A helper function to switch to a new tab at specified position. fn switch_active_tab(&mut self, new_tab_pos: usize, client_id: ClientId) { @@ -423,6 +428,11 @@ impl Screen { pub fn new_tab(&mut self, layout: Layout, new_pids: Vec<RawFd>, client_id: ClientId) { let tab_index = self.get_new_tab_index(); let position = self.tabs.len(); + let client_mode_info = self + .mode_info + .get(&client_id) + .unwrap_or(&self.default_mode_info) + .clone(); let mut tab = Tab::new( tab_index, position, @@ -431,16 +441,20 @@ impl Screen { self.bus.os_input.as_ref().unwrap().clone(), self.bus.senders.clone(), self.max_panes, - self.mode_info.clone(), + client_mode_info, self.colors, self.draw_pane_frames, client_id, ); - tab.apply_layout(layout, new_pids, tab_index); + tab.apply_layout(layout, new_pids, tab_index, client_id); if let Some(active_tab) = self.get_active_tab_mut(client_id) { active_tab.visible(false); - let connected_clients = active_tab.drain_connected_clients(); - tab.add_multiple_clients(&connected_clients); + let (connected_clients_in_source_tab, client_mode_infos_in_source_tab) = + active_tab.drain_connected_clients(); + tab.add_multiple_clients( + connected_clients_in_source_tab, + client_mode_infos_in_source_tab, + ); } for (client_id, tab_history) in &mut self.tab_history { let old_active_index = self.active_tab_indices.remove(client_id).unwrap(); @@ -448,6 +462,7 @@ impl Screen { tab_history.retain(|&e| e != tab_index); tab_history.push(old_active_index); } + tab.update_input_modes(); tab.visible(true); self.tabs.insert(tab_index, tab); if !self.active_tab_indices.contains_key(&client_id) { @@ -503,7 +518,11 @@ impl Screen { } self.bus .senders - .send_to_plugin(PluginInstruction::Update(None, Event::TabUpdate(tab_data))) + .send_to_plugin(PluginInstruction::Update( + None, + None, + Event::TabUpdate(tab_data), + )) .unwrap(); } } @@ -526,7 +545,12 @@ impl Screen { self.update_tabs(); } pub fn change_mode(&mut self, mode_info: ModeInfo, client_id: ClientId) { - if self.mode_info.mode == InputMode::Scroll + let previous_mode = self + .mode_info + .get(&client_id) + .unwrap_or(&self.default_mode_info) + .mode; + if previous_mode == InputMode::Scroll && (mode_info.mode == InputMode::Normal || mode_info.mode == InputMode::Locked) { self.get_active_tab_mut(client_id) @@ -534,9 +558,9 @@ impl Screen { .clear_active_terminal_scroll(client_id); } self.colors = mode_info.palette; - self.mode_info = mode_info; + self.mode_info.insert(client_id, mode_info.clone()); for tab in self.tabs.values_mut() { - tab.mode_info = self.mode_info.clone(); + tab.change_mode_info(mode_info.clone(), client_id); tab.mark_active_pane_for_rerender(client_id); } } @@ -1100,6 +1124,7 @@ pub(crate) fn screen_thread_main( } ScreenInstruction::AddClient(client_id) => { screen.add_client(client_id); + screen.update_tabs(); screen.render(); } diff --git a/zellij-server/src/tab.rs b/zellij-server/src/tab.rs index dec0c0977..f684b9962 100644 --- a/zellij-server/src/tab.rs +++ b/zellij-server/src/tab.rs @@ -137,7 +137,8 @@ pub(crate) struct Tab { pub senders: ThreadSenders, synchronize_is_active: bool, should_clear_display_before_rendering: bool, - pub mode_info: ModeInfo, + mode_info: HashMap<ClientId, ModeInfo>, + default_mode_info: ModeInfo, pub colors: Palette, connected_clients: HashSet<ClientId>, draw_pane_frames: bool, @@ -179,7 +180,7 @@ pub trait Pane { fn set_should_render_boundaries(&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, client_id: Option<ClientId>) -> Option<String>; fn render_frame(&mut self, client_id: ClientId, frame_params: FrameParams) -> Option<String>; fn render_fake_cursor( &mut self, @@ -342,7 +343,8 @@ impl Tab { os_api, senders, should_clear_display_before_rendering: false, - mode_info, + mode_info: HashMap::new(), + default_mode_info: mode_info, colors, draw_pane_frames, // at the moment this is hard-coded while the feature is being developed @@ -353,7 +355,13 @@ impl Tab { } } - pub fn apply_layout(&mut self, layout: Layout, new_pids: Vec<RawFd>, tab_index: usize) { + pub fn apply_layout( + &mut self, + layout: Layout, + new_pids: Vec<RawFd>, + tab_index: usize, + client_id: ClientId, + ) { // TODO: this should be an attribute on Screen instead of full_screen_ws let free_space = PaneGeom::default(); self.panes_to_hide.clear(); @@ -384,7 +392,7 @@ impl Tab { let (pid_tx, pid_rx) = channel(); let pane_title = run.location.to_string(); self.senders - .send_to_plugin(PluginInstruction::Load(pid_tx, run, tab_index)) + .send_to_plugin(PluginInstruction::Load(pid_tx, run, tab_index, client_id)) .unwrap(); let pid = pid_rx.recv().unwrap(); let mut new_plugin = PluginPane::new( @@ -395,13 +403,6 @@ impl Tab { ); new_plugin.set_borderless(layout.borderless); self.panes.insert(PaneId::Plugin(pid), Box::new(new_plugin)); - // Send an initial mode update to the newly loaded plugin only! - self.senders - .send_to_plugin(PluginInstruction::Update( - Some(pid), - Event::ModeUpdate(self.mode_info.clone()), - )) - .unwrap(); } else { // there are still panes left to fill, use the pids we received in this method let pid = new_pids.next().unwrap(); // if this crashes it means we got less pids than there are panes in this layout @@ -465,12 +466,30 @@ impl Tab { } } } + pub fn update_input_modes(&mut self) { + // this updates all plugins with the client's input mode + for client_id in &self.connected_clients { + let mode_info = self + .mode_info + .get(client_id) + .unwrap_or(&self.default_mode_info); + self.senders + .send_to_plugin(PluginInstruction::Update( + None, + Some(*client_id), + Event::ModeUpdate(mode_info.clone()), + )) + .unwrap(); + } + } pub fn add_client(&mut self, client_id: ClientId) { match self.connected_clients.iter().next() { Some(first_client_id) => { let first_active_pane_id = *self.active_panes.get(first_client_id).unwrap(); self.connected_clients.insert(client_id); self.active_panes.insert(client_id, first_active_pane_id); + self.mode_info + .insert(client_id, self.default_mode_info.clone()); } None => { let mut pane_ids: Vec<PaneId> = self.panes.keys().copied().collect(); @@ -482,15 +501,32 @@ impl Tab { let first_pane_id = pane_ids.get(0).unwrap(); self.connected_clients.insert(client_id); self.active_panes.insert(client_id, *first_pane_id); + self.mode_info + .insert(client_id, self.default_mode_info.clone()); } } // TODO: we might be able to avoid this, we do this so that newly connected clients will // necessarily get a full render self.set_force_render(); } - pub fn add_multiple_clients(&mut self, client_ids: &[ClientId]) { + pub fn change_mode_info(&mut self, mode_info: ModeInfo, client_id: ClientId) { + self.mode_info.insert(client_id, mode_info); + } + pub fn add_multiple_clients( + &mut self, + client_ids: Vec<ClientId>, + client_mode_infos: Vec<(ClientId, ModeInfo)>, + ) { for client_id in client_ids { - self.add_client(*client_id); + self.add_client(client_id); + } + for (client_id, client_mode_info) in client_mode_infos { + log::info!( + "add_multiple_clients: client_id: {:?}, mode: {:?}", + client_id, + client_mode_info.mode + ); + self.mode_info.insert(client_id, client_mode_info); } } pub fn remove_client(&mut self, client_id: ClientId) { @@ -498,8 +534,12 @@ impl Tab { self.active_panes.remove(&client_id); self.set_force_render(); } - pub fn drain_connected_clients(&mut self) -> Vec<ClientId> { - self.connected_clients.drain().collect() + pub fn drain_connected_clients(&mut self) -> (Vec<ClientId>, Vec<(ClientId, ModeInfo)>) { + let client_mode_info = self.mode_info.drain(); + ( + self.connected_clients.drain().collect(), + client_mode_info.collect(), + ) } pub fn new_pane(&mut self, pid: PaneId, client_id: Option<ClientId>) { self.close_down_to_max_terminals(); @@ -750,7 +790,7 @@ impl Tab { PaneId::Plugin(pid) => { for key in parse_keys(&input_bytes) { self.senders - .send_to_plugin(PluginInstruction::Update(Some(pid), Event::Key(key))) + .send_to_plugin(PluginInstruction::Update(Some(pid), None, Event::Key(key))) .unwrap() } } @@ -926,25 +966,35 @@ impl Tab { let mut client_id_to_boundaries: HashMap<ClientId, Boundaries> = HashMap::new(); self.hide_cursor_and_clear_display_as_needed(output); // render panes and their frames - for pane in self.panes.values_mut() { + for (kind, pane) in self.panes.iter_mut() { if !self.panes_to_hide.contains(&pane.pid()) { - let mut pane_contents_and_ui = PaneContentsAndUi::new( - pane, - output, - self.colors, - &self.active_panes, - self.mode_info.mode, - ); - pane_contents_and_ui.render_pane_contents_for_all_clients(); + let mut pane_contents_and_ui = + PaneContentsAndUi::new(pane, output, self.colors, &self.active_panes); + if let PaneId::Terminal(..) = kind { + pane_contents_and_ui.render_pane_contents_for_all_clients(); + } for &client_id in &self.connected_clients { + let client_mode = self + .mode_info + .get(&client_id) + .unwrap_or(&self.default_mode_info) + .mode; + if let PaneId::Plugin(..) = kind { + pane_contents_and_ui.render_pane_contents_for_client(client_id); + } if self.draw_pane_frames { - pane_contents_and_ui.render_pane_frame(client_id, self.session_is_mirrored); + pane_contents_and_ui.render_pane_frame( + client_id, + client_mode, + self.session_is_mirrored, + ); } else { let boundaries = client_id_to_boundaries .entry(client_id) .or_insert_with(|| Boundaries::new(self.viewport)); pane_contents_and_ui.render_pane_boundaries( |