refactor(clients): support multiple clients in tab/screen rendering infra (#770)

This commit is contained in:
Aram Drevekenin 2021-10-07 15:22:20 +02:00 committed by GitHub
parent 24154e40e0
commit f2401d0b25
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 314 additions and 151 deletions

View file

@ -27,6 +27,7 @@ use crate::{
os_input_output::ServerOsApi, os_input_output::ServerOsApi,
pty::{pty_thread_main, Pty, PtyInstruction}, pty::{pty_thread_main, Pty, PtyInstruction},
screen::{screen_thread_main, ScreenInstruction}, screen::{screen_thread_main, ScreenInstruction},
tab::Output,
thread_bus::{Bus, ThreadSenders}, thread_bus::{Bus, ThreadSenders},
wasm_vm::{wasm_thread_main, PluginInstruction}, wasm_vm::{wasm_thread_main, PluginInstruction},
}; };
@ -59,7 +60,7 @@ pub(crate) enum ServerInstruction {
ClientId, ClientId,
Option<PluginsConfig>, Option<PluginsConfig>,
), ),
Render(Option<String>), Render(Option<Output>),
UnblockInputThread, UnblockInputThread,
ClientExit(ClientId), ClientExit(ClientId),
RemoveClient(ClientId), RemoveClient(ClientId),
@ -261,7 +262,6 @@ pub fn start_server(mut os_input: Box<dyn ServerOsApi>, socket_path: PathBuf) {
os_input.clone(), os_input.clone(),
to_server.clone(), to_server.clone(),
client_attributes, client_attributes,
session_state.clone(),
SessionOptions { SessionOptions {
opts, opts,
layout: layout.clone(), layout: layout.clone(),
@ -289,7 +289,11 @@ pub fn start_server(mut os_input: Box<dyn ServerOsApi>, socket_path: PathBuf) {
.as_ref() .as_ref()
.unwrap() .unwrap()
.senders .senders
.send_to_pty(PtyInstruction::NewTab(default_shell.clone(), tab_layout)) .send_to_pty(PtyInstruction::NewTab(
default_shell.clone(),
tab_layout,
client_id,
))
.unwrap() .unwrap()
}; };
@ -317,6 +321,10 @@ pub fn start_server(mut os_input: Box<dyn ServerOsApi>, socket_path: PathBuf) {
.senders .senders
.send_to_screen(ScreenInstruction::TerminalResize(min_size)) .send_to_screen(ScreenInstruction::TerminalResize(min_size))
.unwrap(); .unwrap();
session_data
.senders
.send_to_screen(ScreenInstruction::AddClient(client_id))
.unwrap();
let default_mode = options.default_mode.unwrap_or_default(); let default_mode = options.default_mode.unwrap_or_default();
let mode_info = let mode_info =
get_mode_info(default_mode, attrs.palette, session_data.capabilities); get_mode_info(default_mode, attrs.palette, session_data.capabilities);
@ -353,6 +361,16 @@ pub fn start_server(mut os_input: Box<dyn ServerOsApi>, socket_path: PathBuf) {
.senders .senders
.send_to_screen(ScreenInstruction::TerminalResize(min_size)) .send_to_screen(ScreenInstruction::TerminalResize(min_size))
.unwrap(); .unwrap();
// we only do this inside this if because it means there are still connected
// clients
session_data
.write()
.unwrap()
.as_ref()
.unwrap()
.senders
.send_to_screen(ScreenInstruction::RemoveClient(client_id))
.unwrap();
} }
if session_state.read().unwrap().clients.is_empty() { if session_state.read().unwrap().clients.is_empty() {
*session_data.write().unwrap() = None; *session_data.write().unwrap() = None;
@ -370,6 +388,16 @@ pub fn start_server(mut os_input: Box<dyn ServerOsApi>, socket_path: PathBuf) {
.senders .senders
.send_to_screen(ScreenInstruction::TerminalResize(min_size)) .send_to_screen(ScreenInstruction::TerminalResize(min_size))
.unwrap(); .unwrap();
// we only do this inside this if because it means there are still connected
// clients
session_data
.write()
.unwrap()
.as_ref()
.unwrap()
.senders
.send_to_screen(ScreenInstruction::RemoveClient(client_id))
.unwrap();
} }
} }
ServerInstruction::DetachSession(client_id) => { ServerInstruction::DetachSession(client_id) => {
@ -384,6 +412,16 @@ pub fn start_server(mut os_input: Box<dyn ServerOsApi>, socket_path: PathBuf) {
.senders .senders
.send_to_screen(ScreenInstruction::TerminalResize(min_size)) .send_to_screen(ScreenInstruction::TerminalResize(min_size))
.unwrap(); .unwrap();
// we only do this inside this if because it means there are still connected
// clients
session_data
.write()
.unwrap()
.as_ref()
.unwrap()
.senders
.send_to_screen(ScreenInstruction::RemoveClient(client_id))
.unwrap();
} }
} }
ServerInstruction::Render(mut output) => { ServerInstruction::Render(mut output) => {
@ -392,8 +430,13 @@ pub fn start_server(mut os_input: Box<dyn ServerOsApi>, socket_path: PathBuf) {
// If `Some(_)`- unwrap it and forward it to the clients to render. // If `Some(_)`- unwrap it and forward it to the clients to render.
// If `None`- Send an exit instruction. This is the case when a user closes the last Tab/Pane. // If `None`- Send an exit instruction. This is the case when a user closes the last Tab/Pane.
if let Some(op) = output.as_mut() { if let Some(op) = output.as_mut() {
for client_id in client_ids { for (client_id, client_render_instruction) in
os_input.send_to_client(client_id, ServerToClientMsg::Render(op.clone())); op.client_render_instructions.iter_mut()
{
os_input.send_to_client(
*client_id,
ServerToClientMsg::Render(client_render_instruction.clone()),
);
} }
} else { } else {
for client_id in client_ids { for client_id in client_ids {
@ -440,7 +483,6 @@ fn init_session(
os_input: Box<dyn ServerOsApi>, os_input: Box<dyn ServerOsApi>,
to_server: SenderWithContext<ServerInstruction>, to_server: SenderWithContext<ServerInstruction>,
client_attributes: ClientAttributes, client_attributes: ClientAttributes,
session_state: Arc<RwLock<SessionState>>,
options: SessionOptions, options: SessionOptions,
) -> SessionMetaData { ) -> SessionMetaData {
let SessionOptions { let SessionOptions {
@ -508,13 +550,7 @@ fn init_session(
let max_panes = opts.max_panes; let max_panes = opts.max_panes;
move || { move || {
screen_thread_main( screen_thread_main(screen_bus, max_panes, client_attributes, config_options);
screen_bus,
max_panes,
client_attributes,
config_options,
session_state,
);
} }
}) })
.unwrap(); .unwrap();

View file

@ -4,7 +4,7 @@ use crate::{
screen::ScreenInstruction, screen::ScreenInstruction,
thread_bus::{Bus, ThreadSenders}, thread_bus::{Bus, ThreadSenders},
wasm_vm::PluginInstruction, wasm_vm::PluginInstruction,
ServerInstruction, ClientId, ServerInstruction,
}; };
use async_std::{ use async_std::{
future::timeout as async_timeout, future::timeout as async_timeout,
@ -36,7 +36,7 @@ pub(crate) enum PtyInstruction {
SpawnTerminalVertically(Option<TerminalAction>), SpawnTerminalVertically(Option<TerminalAction>),
SpawnTerminalHorizontally(Option<TerminalAction>), SpawnTerminalHorizontally(Option<TerminalAction>),
UpdateActivePane(Option<PaneId>), UpdateActivePane(Option<PaneId>),
NewTab(Option<TerminalAction>, Option<TabLayout>), NewTab(Option<TerminalAction>, Option<TabLayout>, ClientId),
ClosePane(PaneId), ClosePane(PaneId),
CloseTab(Vec<PaneId>), CloseTab(Vec<PaneId>),
Exit, Exit,
@ -96,7 +96,7 @@ pub(crate) fn pty_thread_main(mut pty: Pty, layout: LayoutFromYaml) {
PtyInstruction::UpdateActivePane(pane_id) => { PtyInstruction::UpdateActivePane(pane_id) => {
pty.set_active_pane(pane_id); pty.set_active_pane(pane_id);
} }
PtyInstruction::NewTab(terminal_action, tab_layout) => { PtyInstruction::NewTab(terminal_action, tab_layout, client_id) => {
let tab_name = tab_layout.as_ref().and_then(|layout| { let tab_name = tab_layout.as_ref().and_then(|layout| {
if layout.name.is_empty() { if layout.name.is_empty() {
None None
@ -109,7 +109,7 @@ pub(crate) fn pty_thread_main(mut pty: Pty, layout: LayoutFromYaml) {
let layout: Layout = let layout: Layout =
Layout::try_from(merged_layout).unwrap_or_else(|err| panic!("{}", err)); Layout::try_from(merged_layout).unwrap_or_else(|err| panic!("{}", err));
pty.spawn_terminals_for_layout(layout, terminal_action.clone()); pty.spawn_terminals_for_layout(layout, terminal_action.clone(), client_id);
if let Some(tab_name) = tab_name { if let Some(tab_name) = tab_name {
// clear current name at first // clear current name at first
@ -284,6 +284,7 @@ impl Pty {
&mut self, &mut self,
layout: Layout, layout: Layout,
default_shell: Option<TerminalAction>, 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());
let extracted_run_instructions = layout.extract_run_instructions(); let extracted_run_instructions = layout.extract_run_instructions();
@ -313,9 +314,10 @@ impl Pty {
} }
self.bus self.bus
.senders .senders
.send_to_screen(ScreenInstruction::ApplyLayout( .send_to_screen(ScreenInstruction::NewTab(
layout, layout,
new_pane_pids.clone(), new_pane_pids.clone(),
client_id,
)) ))
.unwrap(); .unwrap();
for id in new_pane_pids { for id in new_pane_pids {

View file

@ -204,7 +204,7 @@ fn route_action(
let shell = session.default_shell.clone(); let shell = session.default_shell.clone();
session session
.senders .senders
.send_to_pty(PtyInstruction::NewTab(shell, tab_layout)) .send_to_pty(PtyInstruction::NewTab(shell, tab_layout, client_id))
.unwrap(); .unwrap();
} }
Action::GoToNextTab => { Action::GoToNextTab => {

View file

@ -3,7 +3,6 @@
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::os::unix::io::RawFd; use std::os::unix::io::RawFd;
use std::str; use std::str;
use std::sync::{Arc, RwLock};
use zellij_utils::pane_size::Size; use zellij_utils::pane_size::Size;
use zellij_utils::{input::layout::Layout, position::Position, zellij_tile}; use zellij_utils::{input::layout::Layout, position::Position, zellij_tile};
@ -14,7 +13,7 @@ use crate::{
tab::Tab, tab::Tab,
thread_bus::Bus, thread_bus::Bus,
wasm_vm::PluginInstruction, wasm_vm::PluginInstruction,
ServerInstruction, SessionState, ClientId, ServerInstruction,
}; };
use zellij_tile::data::{Event, InputMode, ModeInfo, Palette, PluginCapabilities, TabInfo}; use zellij_tile::data::{Event, InputMode, ModeInfo, Palette, PluginCapabilities, TabInfo};
use zellij_utils::{ use zellij_utils::{
@ -59,7 +58,7 @@ pub(crate) enum ScreenInstruction {
TogglePaneFrames, TogglePaneFrames,
SetSelectable(PaneId, bool, usize), SetSelectable(PaneId, bool, usize),
ClosePane(PaneId), ClosePane(PaneId),
ApplyLayout(Layout, Vec<RawFd>), NewTab(Layout, Vec<RawFd>, ClientId),
SwitchTabNext, SwitchTabNext,
SwitchTabPrev, SwitchTabPrev,
ToggleActiveSyncTab, ToggleActiveSyncTab,
@ -73,6 +72,8 @@ pub(crate) enum ScreenInstruction {
MouseRelease(Position), MouseRelease(Position),
MouseHold(Position), MouseHold(Position),
Copy, Copy,
AddClient(ClientId),
RemoveClient(ClientId),
} }
impl From<&ScreenInstruction> for ScreenContext { impl From<&ScreenInstruction> for ScreenContext {
@ -113,7 +114,7 @@ impl From<&ScreenInstruction> for ScreenContext {
ScreenInstruction::TogglePaneFrames => ScreenContext::TogglePaneFrames, ScreenInstruction::TogglePaneFrames => ScreenContext::TogglePaneFrames,
ScreenInstruction::SetSelectable(..) => ScreenContext::SetSelectable, ScreenInstruction::SetSelectable(..) => ScreenContext::SetSelectable,
ScreenInstruction::ClosePane(_) => ScreenContext::ClosePane, ScreenInstruction::ClosePane(_) => ScreenContext::ClosePane,
ScreenInstruction::ApplyLayout(..) => ScreenContext::ApplyLayout, ScreenInstruction::NewTab(..) => ScreenContext::NewTab,
ScreenInstruction::SwitchTabNext => ScreenContext::SwitchTabNext, ScreenInstruction::SwitchTabNext => ScreenContext::SwitchTabNext,
ScreenInstruction::SwitchTabPrev => ScreenContext::SwitchTabPrev, ScreenInstruction::SwitchTabPrev => ScreenContext::SwitchTabPrev,
ScreenInstruction::CloseTab => ScreenContext::CloseTab, ScreenInstruction::CloseTab => ScreenContext::CloseTab,
@ -129,6 +130,8 @@ impl From<&ScreenInstruction> for ScreenContext {
ScreenInstruction::MouseHold(_) => ScreenContext::MouseHold, ScreenInstruction::MouseHold(_) => ScreenContext::MouseHold,
ScreenInstruction::Copy => ScreenContext::Copy, ScreenInstruction::Copy => ScreenContext::Copy,
ScreenInstruction::ToggleTab => ScreenContext::ToggleTab, ScreenInstruction::ToggleTab => ScreenContext::ToggleTab,
ScreenInstruction::AddClient(..) => ScreenContext::AddClient,
ScreenInstruction::RemoveClient(..) => ScreenContext::RemoveClient,
} }
} }
} }
@ -149,7 +152,6 @@ pub(crate) struct Screen {
tab_history: Vec<Option<usize>>, tab_history: Vec<Option<usize>>,
mode_info: ModeInfo, mode_info: ModeInfo,
colors: Palette, colors: Palette,
session_state: Arc<RwLock<SessionState>>,
draw_pane_frames: bool, draw_pane_frames: bool,
} }
@ -160,7 +162,6 @@ impl Screen {
client_attributes: &ClientAttributes, client_attributes: &ClientAttributes,
max_panes: Option<usize>, max_panes: Option<usize>,
mode_info: ModeInfo, mode_info: ModeInfo,
session_state: Arc<RwLock<SessionState>>,
draw_pane_frames: bool, draw_pane_frames: bool,
) -> Self { ) -> Self {
Screen { Screen {
@ -172,7 +173,6 @@ impl Screen {
tabs: BTreeMap::new(), tabs: BTreeMap::new(),
tab_history: Vec::with_capacity(32), tab_history: Vec::with_capacity(32),
mode_info, mode_info,
session_state,
draw_pane_frames, draw_pane_frames,
} }
} }
@ -188,6 +188,14 @@ impl Screen {
} }
} }
fn move_clients(&mut self, source_index: usize, destination_index: usize) {
let connected_clients_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);
}
/// A helper function to switch to a new tab at specified position. /// A helper function to switch to a new tab at specified position.
fn switch_active_tab(&mut self, new_tab_pos: usize) { fn switch_active_tab(&mut self, new_tab_pos: usize) {
if let Some(new_tab) = self.tabs.values().find(|t| t.position == new_tab_pos) { if let Some(new_tab) = self.tabs.values().find(|t| t.position == new_tab_pos) {
@ -199,6 +207,7 @@ impl Screen {
} }
current_tab.visible(false); current_tab.visible(false);
let current_tab_index = current_tab.index;
let new_tab_index = new_tab.index; let new_tab_index = new_tab.index;
let new_tab = self.get_indexed_tab_mut(new_tab_index).unwrap(); let new_tab = self.get_indexed_tab_mut(new_tab_index).unwrap();
new_tab.set_force_render(); new_tab.set_force_render();
@ -214,6 +223,8 @@ impl Screen {
self.tab_history.retain(|&e| e != Some(new_tab_pos)); self.tab_history.retain(|&e| e != Some(new_tab_pos));
self.tab_history.push(old_active_index); self.tab_history.push(old_active_index);
self.move_clients(current_tab_index, new_tab_index);
self.update_tabs(); self.update_tabs();
self.render(); self.render();
} }
@ -295,7 +306,12 @@ impl Screen {
pub fn render(&mut self) { pub fn render(&mut self) {
if let Some(active_tab) = self.get_active_tab_mut() { if let Some(active_tab) = self.get_active_tab_mut() {
if active_tab.get_active_pane().is_some() { if active_tab.get_active_pane().is_some() {
active_tab.render(); if let Some(output) = active_tab.render() {
self.bus
.senders
.send_to_server(ServerInstruction::Render(Some(output)))
.unwrap();
}
} else { } else {
self.close_tab(); self.close_tab();
} }
@ -341,7 +357,7 @@ impl Screen {
/// Creates a new [`Tab`] in this [`Screen`], applying the specified [`Layout`] /// Creates a new [`Tab`] in this [`Screen`], applying the specified [`Layout`]
/// and switching to it. /// and switching to it.
pub fn apply_layout(&mut self, layout: Layout, new_pids: Vec<RawFd>) { pub fn new_tab(&mut self, layout: Layout, new_pids: Vec<RawFd>, client_id: ClientId) {
let tab_index = self.get_new_tab_index(); let tab_index = self.get_new_tab_index();
let position = self.tabs.len(); let position = self.tabs.len();
let mut tab = Tab::new( let mut tab = Tab::new(
@ -354,19 +370,30 @@ impl Screen {
self.max_panes, self.max_panes,
self.mode_info.clone(), self.mode_info.clone(),
self.colors, self.colors,
self.session_state.clone(),
self.draw_pane_frames, self.draw_pane_frames,
client_id,
); );
tab.apply_layout(layout, new_pids, tab_index); tab.apply_layout(layout, new_pids, tab_index);
if let Some(active_tab) = self.get_active_tab_mut() { if let Some(active_tab) = self.get_active_tab_mut() {
active_tab.visible(false); active_tab.visible(false);
active_tab.is_active = false; active_tab.is_active = false;
let connected_clients = active_tab.drain_connected_clients();
tab.add_multiple_clients(&connected_clients);
} }
self.tab_history self.tab_history
.push(self.active_tab_index.replace(tab_index)); .push(self.active_tab_index.replace(tab_index));
tab.visible(true); tab.visible(true);
self.tabs.insert(tab_index, tab); self.tabs.insert(tab_index, tab);
self.update_tabs(); self.update_tabs();
self.render();
}
pub fn add_client(&mut self, client_id: ClientId) {
self.get_active_tab_mut().unwrap().add_client(client_id);
}
pub fn remove_client(&mut self, client_id: ClientId) {
self.get_active_tab_mut().unwrap().remove_client(client_id);
} }
pub fn update_tabs(&self) { pub fn update_tabs(&self) {
@ -450,7 +477,6 @@ pub(crate) fn screen_thread_main(
max_panes: Option<usize>, max_panes: Option<usize>,
client_attributes: ClientAttributes, client_attributes: ClientAttributes,
config_options: Box<Options>, config_options: Box<Options>,
session_state: Arc<RwLock<SessionState>>,
) { ) {
let capabilities = config_options.simplified_ui; let capabilities = config_options.simplified_ui;
let draw_pane_frames = !config_options.no_pane_frames; let draw_pane_frames = !config_options.no_pane_frames;
@ -466,7 +492,6 @@ pub(crate) fn screen_thread_main(
arrow_fonts: capabilities, arrow_fonts: capabilities,
}, },
), ),
session_state,
draw_pane_frames, draw_pane_frames,
); );
loop { loop {
@ -505,6 +530,8 @@ pub(crate) fn screen_thread_main(
.send_to_server(ServerInstruction::UnblockInputThread) .send_to_server(ServerInstruction::UnblockInputThread)
.unwrap(); .unwrap();
screen.update_tabs(); screen.update_tabs();
screen.render();
} }
ScreenInstruction::HorizontalSplit(pid) => { ScreenInstruction::HorizontalSplit(pid) => {
screen.get_active_tab_mut().unwrap().horizontal_split(pid); screen.get_active_tab_mut().unwrap().horizontal_split(pid);
@ -514,6 +541,8 @@ pub(crate) fn screen_thread_main(
.send_to_server(ServerInstruction::UnblockInputThread) .send_to_server(ServerInstruction::UnblockInputThread)
.unwrap(); .unwrap();
screen.update_tabs(); screen.update_tabs();
screen.render();
} }
ScreenInstruction::VerticalSplit(pid) => { ScreenInstruction::VerticalSplit(pid) => {
screen.get_active_tab_mut().unwrap().vertical_split(pid); screen.get_active_tab_mut().unwrap().vertical_split(pid);
@ -523,6 +552,8 @@ pub(crate) fn screen_thread_main(
.send_to_server(ServerInstruction::UnblockInputThread) .send_to_server(ServerInstruction::UnblockInputThread)
.unwrap(); .unwrap();
screen.update_tabs(); screen.update_tabs();
screen.render();
} }
ScreenInstruction::WriteCharacter(bytes) => { ScreenInstruction::WriteCharacter(bytes) => {
let active_tab = screen.get_active_tab_mut().unwrap(); let active_tab = screen.get_active_tab_mut().unwrap();
@ -533,27 +564,43 @@ pub(crate) fn screen_thread_main(
} }
ScreenInstruction::ResizeLeft => { ScreenInstruction::ResizeLeft => {
screen.get_active_tab_mut().unwrap().resize_left(); screen.get_active_tab_mut().unwrap().resize_left();
screen.render();
} }
ScreenInstruction::ResizeRight => { ScreenInstruction::ResizeRight => {
screen.get_active_tab_mut().unwrap().resize_right(); screen.get_active_tab_mut().unwrap().resize_right();
screen.render();
} }
ScreenInstruction::ResizeDown => { ScreenInstruction::ResizeDown => {
screen.get_active_tab_mut().unwrap().resize_down(); screen.get_active_tab_mut().unwrap().resize_down();
screen.render();
} }
ScreenInstruction::ResizeUp => { ScreenInstruction::ResizeUp => {
screen.get_active_tab_mut().unwrap().resize_up(); screen.get_active_tab_mut().unwrap().resize_up();
screen.render();
} }
ScreenInstruction::SwitchFocus => { ScreenInstruction::SwitchFocus => {
screen.get_active_tab_mut().unwrap().move_focus(); screen.get_active_tab_mut().unwrap().move_focus();
screen.render();
} }
ScreenInstruction::FocusNextPane => { ScreenInstruction::FocusNextPane => {
screen.get_active_tab_mut().unwrap().focus_next_pane(); screen.get_active_tab_mut().unwrap().focus_next_pane();
screen.render();
} }
ScreenInstruction::FocusPreviousPane => { ScreenInstruction::FocusPreviousPane => {
screen.get_active_tab_mut().unwrap().focus_previous_pane(); screen.get_active_tab_mut().unwrap().focus_previous_pane();
screen.render();
} }
ScreenInstruction::MoveFocusLeft => { ScreenInstruction::MoveFocusLeft => {
screen.get_active_tab_mut().unwrap().move_focus_left(); screen.get_active_tab_mut().unwrap().move_focus_left();
screen.render();
} }
ScreenInstruction::MoveFocusLeftOrPreviousTab => { ScreenInstruction::MoveFocusLeftOrPreviousTab => {
screen.move_focus_left_or_previous_tab(); screen.move_focus_left_or_previous_tab();
@ -562,12 +609,18 @@ pub(crate) fn screen_thread_main(
.senders .senders
.send_to_server(ServerInstruction::UnblockInputThread) .send_to_server(ServerInstruction::UnblockInputThread)
.unwrap(); .unwrap();
screen.render();
} }
ScreenInstruction::MoveFocusDown => { ScreenInstruction::MoveFocusDown => {
screen.get_active_tab_mut().unwrap().move_focus_down(); screen.get_active_tab_mut().unwrap().move_focus_down();
screen.render();
} }
ScreenInstruction::MoveFocusRight => { ScreenInstruction::MoveFocusRight => {
screen.get_active_tab_mut().unwrap().move_focus_right(); screen.get_active_tab_mut().unwrap().move_focus_right();
screen.render();
} }
ScreenInstruction::MoveFocusRightOrNextTab => { ScreenInstruction::MoveFocusRightOrNextTab => {
screen.move_focus_right_or_next_tab(); screen.move_focus_right_or_next_tab();
@ -576,61 +629,81 @@ pub(crate) fn screen_thread_main(
.senders .senders
.send_to_server(ServerInstruction::UnblockInputThread) .send_to_server(ServerInstruction::UnblockInputThread)
.unwrap(); .unwrap();
screen.render();
} }
ScreenInstruction::MoveFocusUp => { ScreenInstruction::MoveFocusUp => {
screen.get_active_tab_mut().unwrap().move_focus_up(); screen.get_active_tab_mut().unwrap().move_focus_up();
screen.render();
} }
ScreenInstruction::ScrollUp => { ScreenInstruction::ScrollUp => {
screen screen
.get_active_tab_mut() .get_active_tab_mut()
.unwrap() .unwrap()
.scroll_active_terminal_up(); .scroll_active_terminal_up();
screen.render();
} }
ScreenInstruction::ScrollUpAt(point) => { ScreenInstruction::ScrollUpAt(point) => {
screen screen
.get_active_tab_mut() .get_active_tab_mut()
.unwrap() .unwrap()
.scroll_terminal_up(&point, 3); .scroll_terminal_up(&point, 3);
screen.render();
} }
ScreenInstruction::ScrollDown => { ScreenInstruction::ScrollDown => {
screen screen
.get_active_tab_mut() .get_active_tab_mut()
.unwrap() .unwrap()
.scroll_active_terminal_down(); .scroll_active_terminal_down();
screen.render();
} }
ScreenInstruction::ScrollDownAt(point) => { ScreenInstruction::ScrollDownAt(point) => {
screen screen
.get_active_tab_mut() .get_active_tab_mut()
.unwrap() .unwrap()
.scroll_terminal_down(&point, 3); .scroll_terminal_down(&point, 3);
screen.render();
} }
ScreenInstruction::ScrollToBottom => { ScreenInstruction::ScrollToBottom => {
screen screen
.get_active_tab_mut() .get_active_tab_mut()
.unwrap() .unwrap()
.scroll_active_terminal_to_bottom(); .scroll_active_terminal_to_bottom();
screen.render();
} }
ScreenInstruction::PageScrollUp => { ScreenInstruction::PageScrollUp => {
screen screen
.get_active_tab_mut() .get_active_tab_mut()
.unwrap() .unwrap()
.scroll_active_terminal_up_page(); .scroll_active_terminal_up_page();
screen.render();
} }
ScreenInstruction::PageScrollDown => { ScreenInstruction::PageScrollDown => {
screen screen
.get_active_tab_mut() .get_active_tab_mut()
.unwrap() .unwrap()
.scroll_active_terminal_down_page(); .scroll_active_terminal_down_page();
screen.render();
} }
ScreenInstruction::ClearScroll => { ScreenInstruction::ClearScroll => {
screen screen
.get_active_tab_mut() .get_active_tab_mut()
.unwrap() .unwrap()
.clear_active_terminal_scroll(); .clear_active_terminal_scroll();
screen.render();
} }
ScreenInstruction::CloseFocusedPane => { ScreenInstruction::CloseFocusedPane => {
screen.get_active_tab_mut().unwrap().close_focused_pane(); screen.get_active_tab_mut().unwrap().close_focused_pane();
screen.update_tabs(); screen.update_tabs(); // update_tabs eventually calls render through the plugin thread
} }
ScreenInstruction::SetSelectable(id, selectable, tab_index) => { ScreenInstruction::SetSelectable(id, selectable, tab_index) => {
screen.get_indexed_tab_mut(tab_index).map_or_else( screen.get_indexed_tab_mut(tab_index).map_or_else(
@ -643,6 +716,8 @@ pub(crate) fn screen_thread_main(
}, },
|tab| tab.set_pane_selectable(id, selectable), |tab| tab.set_pane_selectable(id, selectable),
); );
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);
@ -654,6 +729,8 @@ pub(crate) fn screen_thread_main(
.unwrap() .unwrap()
.toggle_active_pane_fullscreen(); .toggle_active_pane_fullscreen();
screen.update_tabs(); screen.update_tabs();
screen.render();
} }
ScreenInstruction::TogglePaneFrames => { ScreenInstruction::TogglePaneFrames => {
screen.draw_pane_frames = !screen.draw_pane_frames; screen.draw_pane_frames = !screen.draw_pane_frames;
@ -669,6 +746,8 @@ pub(crate) fn screen_thread_main(
.senders .senders
.send_to_server(ServerInstruction::UnblockInputThread) .send_to_server(ServerInstruction::UnblockInputThread)
.unwrap(); .unwrap();
screen.render();
} }
ScreenInstruction::SwitchTabPrev => { ScreenInstruction::SwitchTabPrev => {
screen.switch_tab_prev(); screen.switch_tab_prev();
@ -677,6 +756,8 @@ pub(crate) fn screen_thread_main(
.senders .senders
.send_to_server(ServerInstruction::UnblockInputThread) .send_to_server(ServerInstruction::UnblockInputThread)
.unwrap(); .unwrap();
screen.render();
} }
ScreenInstruction::CloseTab => { ScreenInstruction::CloseTab => {
screen.close_tab(); screen.close_tab();
@ -685,14 +766,18 @@ pub(crate) fn screen_thread_main(
.senders .senders
.send_to_server(ServerInstruction::UnblockInputThread) .send_to_server(ServerInstruction::UnblockInputThread)
.unwrap(); .unwrap();
screen.render();
} }
ScreenInstruction::ApplyLayout(layout, new_pane_pids) => { ScreenInstruction::NewTab(layout, new_pane_pids, client_id) => {
screen.apply_layout(layout, new_pane_pids); screen.new_tab(layout, new_pane_pids, client_id);
screen screen
.bus .bus
.senders .senders
.send_to_server(ServerInstruction::UnblockInputThread) .send_to_server(ServerInstruction::UnblockInputThread)
.unwrap(); .unwrap();
screen.render();
} }
ScreenInstruction::GoToTab(tab_index) => { ScreenInstruction::GoToTab(tab_index) => {
screen.go_to_tab(tab_index as usize); screen.go_to_tab(tab_index as usize);
@ -701,15 +786,23 @@ pub(crate) fn screen_thread_main(
.senders .senders
.send_to_server(ServerInstruction::UnblockInputThread) .send_to_server(ServerInstruction::UnblockInputThread)
.unwrap(); .unwrap();
screen.render();
} }
ScreenInstruction::UpdateTabName(c) => { ScreenInstruction::UpdateTabName(c) => {
screen.update_active_tab_name(c); screen.update_active_tab_name(c);
screen.render();
} }
ScreenInstruction::TerminalResize(new_size) => { ScreenInstruction::TerminalResize(new_size) => {
screen.resize_to_screen(new_size); screen.resize_to_screen(new_size);
screen.render();
} }
ScreenInstruction::ChangeMode(mode_info) => { ScreenInstruction::ChangeMode(mode_info) => {
screen.change_mode(mode_info); screen.change_mode(mode_info);
screen.render();
} }
ScreenInstruction::ToggleActiveSyncTab => { ScreenInstruction::ToggleActiveSyncTab => {
screen screen
@ -717,27 +810,37 @@ pub(crate) fn screen_thread_main(
.unwrap() .unwrap()
.toggle_sync_panes_is_active(); .toggle_sync_panes_is_active();
screen.update_tabs(); screen.update_tabs();
screen.render();
} }
ScreenInstruction::LeftClick(point) => { ScreenInstruction::LeftClick(point) => {
screen screen
.get_active_tab_mut() .get_active_tab_mut()
.unwrap() .unwrap()
.handle_left_click(&point); .handle_left_click(&point);
screen.render();
} }
ScreenInstruction::MouseRelease(point) => { ScreenInstruction::MouseRelease(point) => {
screen screen
.get_active_tab_mut() .get_active_tab_mut()
.unwrap() .unwrap()
.handle_mouse_release(&point); .handle_mouse_release(&point);
screen.render();
} }
ScreenInstruction::MouseHold(point) => { ScreenInstruction::MouseHold(point) => {
screen screen
.get_active_tab_mut() .get_active_tab_mut()
.unwrap() .unwrap()
.handle_mouse_hold(&point); .handle_mouse_hold(&point);
screen.render();
} }
ScreenInstruction::Copy => { ScreenInstruction::Copy => {
screen.get_active_tab().unwrap().copy_selection(); screen.get_active_tab().unwrap().copy_selection();
screen.render();
} }
ScreenInstruction::Exit => { ScreenInstruction::Exit => {
break; break;
@ -749,6 +852,18 @@ pub(crate) fn screen_thread_main(
.senders .senders
.send_to_server(ServerInstruction::UnblockInputThread) .send_to_server(ServerInstruction::UnblockInputThread)
.unwrap(); .unwrap();
screen.render();
}
ScreenInstruction::AddClient(client_id) => {
screen.add_client(client_id);
screen.render();
}
ScreenInstruction::RemoveClient(client_id) => {
screen.remove_client(client_id);
screen.render();
} }
} }
} }

View file

@ -8,11 +8,11 @@ use crate::{
thread_bus::ThreadSenders, thread_bus::ThreadSenders,
ui::boundaries::Boundaries, ui::boundaries::Boundaries,
wasm_vm::PluginInstruction, wasm_vm::PluginInstruction,
ServerInstruction, SessionState, ClientId, ServerInstruction,
}; };
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::os::unix::io::RawFd; use std::os::unix::io::RawFd;
use std::sync::{mpsc::channel, Arc, RwLock}; use std::sync::mpsc::channel;
use std::time::Instant; use std::time::Instant;
use std::{ use std::{
cmp::Reverse, cmp::Reverse,
@ -40,7 +40,6 @@ const MIN_TERMINAL_WIDTH: usize = 5;
const RESIZE_PERCENT: f64 = 5.0; const RESIZE_PERCENT: f64 = 5.0;
const MAX_PENDING_VTE_EVENTS: usize = 7000; const MAX_PENDING_VTE_EVENTS: usize = 7000;
const PENDING_EVENTS_SET_SIZE: usize = 200;
type BorderAndPaneIds = (usize, Vec<PaneId>); type BorderAndPaneIds = (usize, Vec<PaneId>);
@ -97,6 +96,28 @@ fn pane_content_offset(position_and_size: &PaneGeom, viewport: &Viewport) -> (us
(columns_offset, rows_offset) (columns_offset, rows_offset)
} }
#[derive(Clone, Debug)]
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();
for client_id in client_ids {
client_render_instructions.insert(*client_id, String::new());
}
Output {
client_render_instructions,
}
}
pub fn push_str_to_all_clients(&mut self, to_push: &str) {
for render_instruction in self.client_render_instructions.values_mut() {
render_instruction.push_str(to_push)
}
}
}
pub(crate) struct Tab { pub(crate) struct Tab {
pub index: usize, pub index: usize,
pub position: usize, pub position: usize,
@ -112,10 +133,10 @@ pub(crate) struct Tab {
pub senders: ThreadSenders, pub senders: ThreadSenders,
synchronize_is_active: bool, synchronize_is_active: bool,
should_clear_display_before_rendering: bool, should_clear_display_before_rendering: bool,
session_state: Arc<RwLock<SessionState>>,
pub mode_info: ModeInfo, pub mode_info: ModeInfo,
pub colors: Palette, pub colors: Palette,
pub is_active: bool, pub is_active: bool,
connected_clients: HashSet<ClientId>,
draw_pane_frames: bool, draw_pane_frames: bool,
pending_vte_events: HashMap<RawFd, Vec<VteBytes>>, pending_vte_events: HashMap<RawFd, Vec<VteBytes>>,
} }
@ -268,8 +289,8 @@ impl Tab {
max_panes: Option<usize>, max_panes: Option<usize>,
mode_info: ModeInfo, mode_info: ModeInfo,
colors: Palette, colors: Palette,
session_state: Arc<RwLock<SessionState>>,
draw_pane_frames: bool, draw_pane_frames: bool,
client_id: ClientId,
) -> Self { ) -> Self {
let panes = BTreeMap::new(); let panes = BTreeMap::new();
@ -279,6 +300,9 @@ impl Tab {
name name
}; };
let mut connected_clients = HashSet::new();
connected_clients.insert(client_id);
Tab { Tab {
index, index,
position, position,
@ -296,10 +320,10 @@ impl Tab {
should_clear_display_before_rendering: false, should_clear_display_before_rendering: false,
mode_info, mode_info,
colors, colors,
session_state,
draw_pane_frames, draw_pane_frames,
pending_vte_events: HashMap::new(), pending_vte_events: HashMap::new(),
is_active: true, is_active: true,
connected_clients,
} }
} }
@ -397,7 +421,23 @@ impl Tab {
// This is the end of the nasty viewport hack... // This is the end of the nasty viewport hack...
// FIXME: Active / new / current terminal, should be pane // FIXME: Active / new / current terminal, should be pane
self.active_terminal = self.panes.iter().map(|(id, _)| id.to_owned()).next(); self.active_terminal = self.panes.iter().map(|(id, _)| id.to_owned()).next();
self.render(); }
pub fn add_client(&mut self, client_id: ClientId) {
self.connected_clients.insert(client_id);
// 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]) {
for client_id in client_ids {
self.connected_clients.insert(*client_id);
}
}
pub fn remove_client(&mut self, client_id: ClientId) {
self.connected_clients.remove(&client_id);
}
pub fn drain_connected_clients(&mut self) -> Vec<ClientId> {
self.connected_clients.drain().collect()
} }
pub fn new_pane(&mut self, pid: PaneId) { pub fn new_pane(&mut self, pid: PaneId) {
self.close_down_to_max_terminals(); self.close_down_to_max_terminals();
@ -470,7 +510,6 @@ impl Tab {
} }
} }
self.active_terminal = Some(pid); self.active_terminal = Some(pid);
self.render();
} }
pub fn horizontal_split(&mut self, pid: PaneId) { pub fn horizontal_split(&mut self, pid: PaneId) {
self.close_down_to_max_terminals(); self.close_down_to_max_terminals();
@ -500,7 +539,6 @@ impl Tab {
self.panes.insert(pid, Box::new(new_terminal)); self.panes.insert(pid, Box::new(new_terminal));
self.active_terminal = Some(pid); self.active_terminal = Some(pid);
self.relayout_tab(Direction::Vertical); self.relayout_tab(Direction::Vertical);
self.render();
} }
} }
} }
@ -529,7 +567,6 @@ impl Tab {
} }
self.active_terminal = Some(pid); self.active_terminal = Some(pid);
self.relayout_tab(Direction::Horizontal); self.relayout_tab(Direction::Horizontal);
self.render();
} }
} }
pub fn get_active_pane(&self) -> Option<&dyn Pane> { pub fn get_active_pane(&self) -> Option<&dyn Pane> {
@ -555,10 +592,10 @@ impl Tab {
if terminal_output.is_scrolled() { if terminal_output.is_scrolled() {
self.pending_vte_events.entry(pid).or_default().push(bytes); self.pending_vte_events.entry(pid).or_default().push(bytes);
if let Some(evs) = self.pending_vte_events.get(&pid) { if let Some(evs) = self.pending_vte_events.get(&pid) {
// Reset scroll and play the pending events if the buufer size exceeds the limit // Reset scroll - and process all pending events for this pane
if evs.len() >= MAX_PENDING_VTE_EVENTS { if evs.len() >= MAX_PENDING_VTE_EVENTS {
terminal_output.clear_scroll(); terminal_output.clear_scroll();
self.play_pending_vte_events(pid); self.process_pending_vte_events(pid);
} }
} }
return; return;
@ -566,25 +603,12 @@ impl Tab {
} }
self.process_pty_bytes(pid, bytes); self.process_pty_bytes(pid, bytes);
} }
fn play_pending_vte_events(&mut self, pid: RawFd) { pub fn process_pending_vte_events(&mut self, pid: RawFd) {
if self.pending_vte_events.get(&pid).is_some() { if let Some(pending_vte_events) = self.pending_vte_events.get_mut(&pid) {
if let Some(terminal_output) = self.panes.get_mut(&PaneId::Terminal(pid)) { let vte_events: Vec<VteBytes> = pending_vte_events.drain(..).collect();
if terminal_output.is_scrolled() { for vte_event in vte_events {
return; self.process_pty_bytes(pid, vte_event);
}
} }
let mut events = self.pending_vte_events.remove(&pid).unwrap();
while events.len() >= PENDING_EVENTS_SET_SIZE {
events
.drain(..PENDING_EVENTS_SET_SIZE)
.for_each(|bytes| self.process_pty_bytes(pid, bytes));
// Render at regular intervals
self.render();
}
events
.drain(..)
.for_each(|bytes| self.process_pty_bytes(pid, bytes));
self.render();
} }
} }
fn process_pty_bytes(&mut self, pid: RawFd, bytes: VteBytes) { fn process_pty_bytes(&mut self, pid: RawFd, bytes: VteBytes) {
@ -699,7 +723,6 @@ impl Tab {
} }
self.set_force_render(); self.set_force_render();
self.resize_whole_tab(self.display_area); self.resize_whole_tab(self.display_area);
self.render();
self.toggle_fullscreen_is_active(); self.toggle_fullscreen_is_active();
} }
} }
@ -772,26 +795,20 @@ impl Tab {
} }
} }
} }
pub fn render(&mut self) { pub fn render(&mut self) -> Option<Output> {
if self.active_terminal.is_none() if self.connected_clients.is_empty() || self.active_terminal.is_none() {
|| self.session_state.read().unwrap().clients.is_empty() return None;
|| !self.is_active
{
// we might not have an active terminal if we closed the last pane
// in that case, we should not render as the app is exiting
// or if there are no attached clients to this session
return;
} }
self.senders self.senders
.send_to_pty(PtyInstruction::UpdateActivePane(self.active_terminal)) .send_to_pty(PtyInstruction::UpdateActivePane(self.active_terminal))
.unwrap(); .unwrap();
let mut output = String::new(); let mut output = Output::new(&self.connected_clients);
let mut boundaries = Boundaries::new(self.viewport); let mut boundaries = Boundaries::new(self.viewport);
let hide_cursor = "\u{1b}[?25l"; let hide_cursor = "\u{1b}[?25l";
output.push_str(hide_cursor); output.push_str_to_all_clients(hide_cursor);
if self.should_clear_display_before_rendering { if self.should_clear_display_before_rendering {
let clear_display = "\u{1b}[2J"; let clear_display = "\u{1b}[2J";
output.push_str(clear_display); output.push_str_to_all_clients(clear_display);
self.should_clear_display_before_rendering = false; self.should_clear_display_before_rendering = false;
} }
for (_kind, pane) in self.panes.iter_mut() { for (_kind, pane) in self.panes.iter_mut() {
@ -824,7 +841,7 @@ impl Tab {
} }
if let Some(vte_output) = pane.render() { if let Some(vte_output) = pane.render() {
// FIXME: Use Termion for cursor and style clearing? // FIXME: Use Termion for cursor and style clearing?
output.push_str(&format!( output.push_str_to_all_clients(&format!(
"\u{1b}[{};{}H\u{1b}[m{}", "\u{1b}[{};{}H\u{1b}[m{}",
pane.y() + 1, pane.y() + 1,
pane.x() + 1, pane.x() + 1,
@ -835,7 +852,7 @@ impl Tab {
} }
if !self.draw_pane_frames { if !self.draw_pane_frames {
output.push_str(&boundaries.vte_output()); output.push_str_to_all_clients(&boundaries.vte_output());
} }
match self.get_active_terminal_cursor_position() { match self.get_active_terminal_cursor_position() {
@ -848,18 +865,15 @@ impl Tab {
cursor_position_x + 1, cursor_position_x + 1,
change_cursor_shape change_cursor_shape
); // goto row/col ); // goto row/col
output.push_str(show_cursor); output.push_str_to_all_clients(show_cursor);
output.push_str(goto_cursor_position); output.push_str_to_all_clients(goto_cursor_position);
} }
None => { None => {
let hide_cursor = "\u{1b}[?25l"; let hide_cursor = "\u{1b}[?25l";
output.push_str(hide_cursor); output.push_str_to_all_clients(hide_cursor);
} }
} }
Some(output)
self.senders
.send_to_server(ServerInstruction::Render(Some(output)))
.unwrap();
} }
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()
@ -1757,7 +1771,6 @@ impl Tab {
} }
} }
self.relayout_tab(Direction::Horizontal); self.relayout_tab(Direction::Horizontal);
self.render();
} }
pub fn resize_right(&mut self) { pub fn resize_right(&mut self) {
// TODO: find out by how much we actually reduced and only reduce by that much // TODO: find out by how much we actually reduced and only reduce by that much
@ -1769,7 +1782,6 @@ impl Tab {
} }
} }
self.relayout_tab(Direction::Horizontal); self.relayout_tab(Direction::Horizontal);
self.render();
} }
pub fn resize_down(&mut self) { pub fn resize_down(&mut self) {
// TODO: find out by how much we actually reduced and only reduce by that much // TODO: find out by how much we actually reduced and only reduce by that much
@ -1781,7 +1793,6 @@ impl Tab {
} }
} }
self.relayout_tab(Direction::Vertical); self.relayout_tab(Direction::Vertical);
self.render();
} }
pub fn resize_up(&mut self) { pub fn resize_up(&mut self) {
// TODO: find out by how much we actually reduced and only reduce by that much // TODO: find out by how much we actually reduced and only reduce by that much
@ -1793,7 +1804,6 @@ impl Tab {
} }
} }
self.relayout_tab(Direction::Vertical); self.relayout_tab(Direction::Vertical);
self.render();
} }
pub fn move_focus(&mut self) { pub fn move_focus(&mut self) {
if !self.has_selectable_panes() { if !self.has_selectable_panes() {
@ -1814,7 +1824,6 @@ impl Tab {
.copied(); .copied();
self.active_terminal = active_terminal; self.active_terminal = active_terminal;
self.render();
} }
pub fn focus_next_pane(&mut self) { pub fn focus_next_pane(&mut self) {
if !self.has_selectable_panes() { if !self.has_selectable_panes() {
@ -1843,7 +1852,6 @@ impl Tab {
.map(|p| *p.0); .map(|p| *p.0);
self.active_terminal = active_terminal; self.active_terminal = active_terminal;
self.render();
} }
pub fn focus_previous_pane(&mut self) { pub fn focus_previous_pane(&mut self) {
if !self.has_selectable_panes() { if !self.has_selectable_panes() {
@ -1873,7 +1881,6 @@ impl Tab {
Some(*panes.get(active_pane_position - 1).unwrap().0) Some(*panes.get(active_pane_position - 1).unwrap().0)
}; };
self.active_terminal = active_terminal; self.active_terminal = active_terminal;
self.render();
} }
// returns a boolean that indicates whether the focus moved // returns a boolean that indicates whether the focus moved
pub fn move_focus_left(&mut self) -> bool { pub fn move_focus_left(&mut self) -> bool {
@ -1904,7 +1911,6 @@ impl Tab {
next_active_pane.set_should_render(true); next_active_pane.set_should_render(true);
self.active_terminal = Some(p); self.active_terminal = Some(p);
self.render();
return true; return true;
} }
None => Some(active.pid()), None => Some(active.pid()),
@ -1950,7 +1956,6 @@ impl Tab {
Some(active_terminal.unwrap().pid()) Some(active_terminal.unwrap().pid())
}; };
self.active_terminal = updated_active_terminal; self.active_terminal = updated_active_terminal;
self.render();
} }
pub fn move_focus_up(&mut self) { pub fn move_focus_up(&mut self) {
if !self.has_selectable_panes() { if !self.has_selectable_panes() {
@ -1987,7 +1992,6 @@ impl Tab {
Some(active_terminal.unwrap().pid()) Some(active_terminal.unwrap().pid())
}; };
self.active_terminal = updated_active_terminal; self.active_terminal = updated_active_terminal;
self.render();
} }
// returns a boolean that indicates whether the focus moved // returns a boolean that indicates whether the focus moved
pub fn move_focus_right(&mut self) -> bool { pub fn move_focus_right(&mut self) -> bool {
@ -2018,7 +2022,6 @@ impl Tab {
next_active_pane.set_should_render(true); next_active_pane.set_should_render(true);
self.active_terminal = Some(p); self.active_terminal = Some(p);
self.render();
return true; return true;
} }
None => Some(active.pid()), None => Some(active.pid()),
@ -2175,7 +2178,6 @@ impl Tab {
self.active_terminal = self.next_active_pane(&self.get_pane_ids()); self.active_terminal = self.next_active_pane(&self.get_pane_ids());
} }
} }
self.render();
} }
pub fn close_pane(&mut self, id: PaneId) { pub fn close_pane(&mut self, id: PaneId) {
if self.fullscreen_is_active { if self.fullscreen_is_active {
@ -2256,7 +2258,6 @@ impl Tab {
.get_mut(&PaneId::Terminal(active_terminal_id)) .get_mut(&PaneId::Terminal(active_terminal_id))
.unwrap(); .unwrap();
active_terminal.scroll_up(1); active_terminal.scroll_up(1);
self.render();
} }
} }
pub fn scroll_active_terminal_down(&mut self) { pub fn scroll_active_terminal_down(&mut self) {
@ -2266,8 +2267,9 @@ impl Tab {
.get_mut(&PaneId::Terminal(active_terminal_id)) .get_mut(&PaneId::Terminal(active_terminal_id))
.unwrap(); .unwrap();
active_terminal.scroll_down(1); active_terminal.scroll_down(1);
self.play_pending_vte_events(active_terminal_id); if !active_terminal.is_scrolled() {
self.render(); self.process_pending_vte_events(active_terminal_id);
}
} }
} }
pub fn scroll_active_terminal_up_page(&mut self) { pub fn scroll_active_terminal_up_page(&mut self) {
@ -2279,7 +2281,6 @@ impl Tab {
// prevent overflow when row == 0 // prevent overflow when row == 0
let scroll_columns = active_terminal.rows().max(1) - 1; let scroll_columns = active_terminal.rows().max(1) - 1;
active_terminal.scroll_up(scroll_columns); active_terminal.scroll_up(scroll_columns);
self.render();
} }
} }
pub fn scroll_active_terminal_down_page(&mut self) { pub fn scroll_active_terminal_down_page(&mut self) {
@ -2291,8 +2292,9 @@ impl Tab {
// prevent overflow when row == 0 // prevent overflow when row == 0
let scroll_columns = active_terminal.rows().max(1) - 1; let scroll_columns = active_terminal.rows().max(1) - 1;
active_terminal.scroll_down(scroll_columns); active_terminal.scroll_down(scroll_columns);
self.play_pending_vte_events(active_terminal_id); if !active_terminal.is_scrolled() {
self.render(); self.process_pending_vte_events(active_terminal_id);
}
} }
} }
pub fn scroll_active_terminal_to_bottom(&mut self) { pub fn scroll_active_terminal_to_bottom(&mut self) {
@ -2302,8 +2304,9 @@ impl Tab {
.get_mut(&PaneId::Terminal(active_terminal_id)) .get_mut(&PaneId::Terminal(active_terminal_id))
.unwrap(); .unwrap();
active_terminal.clear_scroll(); active_terminal.clear_scroll();
self.play_pending_vte_events(active_terminal_id); if !active_terminal.is_scrolled() {
self.render(); self.process_pending_vte_events(active_terminal_id);
}
} }
} }
pub fn clear_active_terminal_scroll(&mut self) { pub fn clear_active_terminal_scroll(&mut self) {
@ -2313,22 +2316,24 @@ impl Tab {
.get_mut(&PaneId::Terminal(active_terminal_id)) .get_mut(&PaneId::Terminal(active_terminal_id))
.unwrap(); .unwrap();
active_terminal.clear_scroll(); active_terminal.clear_scroll();
self.play_pending_vte_events(active_terminal_id); if !active_terminal.is_scrolled() {
self.process_pending_vte_events(active_terminal_id);
}
} }
} }
pub fn scroll_terminal_up(&mut self, point: &Position, lines: usize) { pub fn scroll_terminal_up(&mut self, point: &Position, lines: usize) {
if let Some(pane) = self.get_pane_at(point) { if let Some(pane) = self.get_pane_at(point) {
pane.scroll_up(lines); pane.scroll_up(lines);
self.render();
} }
} }
pub fn scroll_terminal_down(&mut self, point: &Position, lines: usize) { pub fn scroll_terminal_down(&mut self, point: &Position, lines: usize) {
if let Some(pane) = self.get_pane_at(point) { if let Some(pane) = self.get_pane_at(point) {
pane.scroll_down(lines); pane.scroll_down(lines);
if let PaneId::Terminal(id) = pane.pid() { if !pane.is_scrolled() {
self.play_pending_vte_events(id); if let PaneId::Terminal(pid) = pane.pid() {
self.process_pending_vte_events(pid);
}
} }
self.render();
} }
} }
fn get_pane_at(&mut self, point: &Position) -> Option<&mut Box<dyn Pane>> { fn get_pane_at(&mut self, point: &Position) -> Option<&mut Box<dyn Pane>> {
@ -2353,13 +2358,11 @@ impl Tab {
if let Some(pane) = self.get_pane_at(position) { if let Some(pane) = self.get_pane_at(position) {
let relative_position = pane.relative_position(position); let relative_position = pane.relative_position(position);
pane.start_selection(&relative_position); pane.start_selection(&relative_position);
self.render();
}; };
} }
fn focus_pane_at(&mut self, point: &Position) { fn focus_pane_at(&mut self, point: &Position) {
if let Some(clicked_pane) = self.get_pane_id_at(point) { if let Some(clicked_pane) = self.get_pane_id_at(point) {
self.active_terminal = Some(clicked_pane); self.active_terminal = Some(clicked_pane);
self.render();
} }
} }
pub fn handle_mouse_release(&mut self, position: &Position) { pub fn handle_mouse_release(&mut self, position: &Position) {
@ -2372,7 +2375,6 @@ impl Tab {
active_pane.end_selection(None); active_pane.end_selection(None);
selected_text = active_pane.get_selected_text(); selected_text = active_pane.get_selected_text();
active_pane.reset_selection(); active_pane.reset_selection();
self.render();
} }
} }
} else if let Some(pane) = self.get_pane_at(position) { } else if let Some(pane) = self.get_pane_at(position) {
@ -2380,7 +2382,6 @@ impl Tab {
pane.end_selection(Some(&relative_position)); pane.end_selection(Some(&relative_position));
selected_text = pane.get_selected_text(); selected_text = pane.get_selected_text();
pane.reset_selection(); pane.reset_selection();
self.render();
} }
if let Some(selected_text) = selected_text { if let Some(selected_text) = selected_text {
@ -2394,7 +2395,6 @@ impl Tab {
active_pane.update_selection(&relative_position); active_pane.update_selection(&relative_position);
} }
} }
self.render();
} }
pub fn copy_selection(&self) { pub fn copy_selection(&self) {
@ -2408,7 +2408,13 @@ impl Tab {
} }
fn write_selection_to_clipboard(&self, selection: &str) { fn write_selection_to_clipboard(&self, selection: &str) {
let output = format!("\u{1b}]52;c;{}\u{1b}\\", base64::encode(selection)); let mut output = Output::new(&self.connected_clients);
output.push_str_to_all_clients(&format!(
"\u{1b}]52;c;{}\u{1b}\\",
base64::encode(selection)
));
// TODO: ideally we should be sending the Render instruction from the screen
self.senders self.senders
.send_to_server(ServerInstruction::Render(Some(output))) .send_to_server(ServerInstruction::Render(Some(output)))
.unwrap(); .unwrap();

View file

@ -3,11 +3,10 @@ use crate::zellij_tile::data::{ModeInfo, Palette};
use crate::{ use crate::{
os_input_output::{AsyncReader, ChildId, Pid, ServerOsApi}, os_input_output::{AsyncReader, ChildId, Pid, ServerOsApi},
thread_bus::Bus, thread_bus::Bus,
ClientId, SessionState, ClientId,
}; };
use std::convert::TryInto; use std::convert::TryInto;
use std::path::PathBuf; use std::path::PathBuf;
use std::sync::{Arc, RwLock};
use zellij_utils::input::command::TerminalAction; use zellij_utils::input::command::TerminalAction;
use zellij_utils::input::layout::LayoutTemplate; use zellij_utils::input::layout::LayoutTemplate;
use zellij_utils::ipc::IpcReceiverWithContext; use zellij_utils::ipc::IpcReceiverWithContext;
@ -27,44 +26,44 @@ use zellij_utils::{
struct FakeInputOutput {} struct FakeInputOutput {}
impl ServerOsApi for FakeInputOutput { impl ServerOsApi for FakeInputOutput {
fn set_terminal_size_using_fd(&self, fd: RawFd, cols: u16, rows: u16) { fn set_terminal_size_using_fd(&self, _fd: RawFd, _cols: u16, _rows: u16) {
// noop // noop
} }
fn spawn_terminal(&self, _file_to_open: TerminalAction) -> (RawFd, ChildId) { fn spawn_terminal(&self, _file_to_open: TerminalAction) -> (RawFd, ChildId) {
unimplemented!() unimplemented!()
} }
fn read_from_tty_stdout(&self, fd: RawFd, buf: &mut [u8]) -> Result<usize, nix::Error> { fn read_from_tty_stdout(&self, _fd: RawFd, _buf: &mut [u8]) -> Result<usize, nix::Error> {
unimplemented!() unimplemented!()
} }
fn async_file_reader(&self, fd: RawFd) -> Box<dyn AsyncReader> { fn async_file_reader(&self, _fd: RawFd) -> Box<dyn AsyncReader> {
unimplemented!() unimplemented!()
} }
fn write_to_tty_stdin(&self, fd: RawFd, buf: &[u8]) -> Result<usize, nix::Error> { fn write_to_tty_stdin(&self, _fd: RawFd, _buf: &[u8]) -> Result<usize, nix::Error> {
unimplemented!() unimplemented!()
} }
fn tcdrain(&self, fd: RawFd) -> Result<(), nix::Error> { fn tcdrain(&self, _fd: RawFd) -> Result<(), nix::Error> {
unimplemented!() unimplemented!()
} }
fn kill(&self, pid: Pid) -> Result<(), nix::Error> { fn kill(&self, _pid: Pid) -> Result<(), nix::Error> {
unimplemented!() unimplemented!()
} }
fn force_kill(&self, pid: Pid) -> Result<(), nix::Error> { fn force_kill(&self, _pid: Pid) -> Result<(), nix::Error> {
unimplemented!() unimplemented!()
} }
fn box_clone(&self) -> Box<dyn ServerOsApi> { fn box_clone(&self) -> Box<dyn ServerOsApi> {
Box::new((*self).clone()) Box::new((*self).clone())
} }
fn send_to_client(&self, client_id: ClientId, msg: ServerToClientMsg) { fn send_to_client(&self, _client_id: ClientId, _msg: ServerToClientMsg) {
unimplemented!() unimplemented!()
} }
fn new_client( fn new_client(
&mut self, &mut self,
client_id: ClientId, _client_id: ClientId,
stream: LocalSocketStream, _stream: LocalSocketStream,
) -> IpcReceiverWithContext<ClientToServerMsg> { ) -> IpcReceiverWithContext<ClientToServerMsg> {
unimplemented!() unimplemented!()
} }
fn remove_client(&mut self, client_id: ClientId) { fn remove_client(&mut self, _client_id: ClientId) {
unimplemented!() unimplemented!()
} }
fn load_palette(&self) -> Palette { fn load_palette(&self) -> Palette {
@ -85,19 +84,23 @@ fn create_new_screen(size: Size) -> Screen {
}; };
let max_panes = None; let max_panes = None;
let mode_info = ModeInfo::default(); let mode_info = ModeInfo::default();
let session_state = Arc::new(RwLock::new(SessionState::new())); let draw_pane_frames = false;
Screen::new( Screen::new(
bus, bus,
&client_attributes, &client_attributes,
max_panes, max_panes,
mode_info, mode_info,
session_state, draw_pane_frames,
false, // draw_pane_frames
) )
} }
fn new_tab(screen: &mut Screen, pid: i32) { fn new_tab(screen: &mut Screen, pid: i32) {
screen.apply_layout(LayoutTemplate::default().try_into().unwrap(), vec![pid]); let client_id = 1;
screen.new_tab(
LayoutTemplate::default().try_into().unwrap(),
vec![pid],
client_id,
);
} }
#[test] #[test]

View file

@ -4,11 +4,10 @@ use crate::{
os_input_output::{AsyncReader, ChildId, Pid, ServerOsApi}, os_input_output::{AsyncReader, ChildId, Pid, ServerOsApi},
panes::PaneId, panes::PaneId,
thread_bus::ThreadSenders, thread_bus::ThreadSenders,
ClientId, SessionState, ClientId,
}; };
use std::convert::TryInto; use std::convert::TryInto;
use std::path::PathBuf; use std::path::PathBuf;
use std::sync::{Arc, RwLock};
use zellij_utils::input::layout::LayoutTemplate; use zellij_utils::input::layout::LayoutTemplate;
use zellij_utils::ipc::IpcReceiverWithContext; use zellij_utils::ipc::IpcReceiverWithContext;
use zellij_utils::pane_size::Size; use zellij_utils::pane_size::Size;
@ -27,44 +26,44 @@ use zellij_utils::{
struct FakeInputOutput {} struct FakeInputOutput {}
impl ServerOsApi for FakeInputOutput { impl ServerOsApi for FakeInputOutput {
fn set_terminal_size_using_fd(&self, fd: RawFd, cols: u16, rows: u16) { fn set_terminal_size_using_fd(&self, _fd: RawFd, _cols: u16, _rows: u16) {
// noop // noop
} }
fn spawn_terminal(&self, _file_to_open: TerminalAction) -> (RawFd, ChildId) { fn spawn_terminal(&self, _file_to_open: TerminalAction) -> (RawFd, ChildId) {
unimplemented!() unimplemented!()
} }
fn read_from_tty_stdout(&self, fd: RawFd, buf: &mut [u8]) -> Result<usize, nix::Error> { fn read_from_tty_stdout(&self, _fd: RawFd, _buf: &mut [u8]) -> Result<usize, nix::Error> {
unimplemented!() unimplemented!()
} }
fn async_file_reader(&self, fd: RawFd) -> Box<dyn AsyncReader> { fn async_file_reader(&self, _fd: RawFd) -> Box<dyn AsyncReader> {
unimplemented!() unimplemented!()
} }
fn write_to_tty_stdin(&self, fd: RawFd, buf: &[u8]) -> Result<usize, nix::Error> { fn write_to_tty_stdin(&self, _fd: RawFd, _buf: &[u8]) -> Result<usize, nix::Error> {
unimplemented!() unimplemented!()
} }
fn tcdrain(&self, fd: RawFd) -> Result<(), nix::Error> { fn tcdrain(&self, _fd: RawFd) -> Result<(), nix::Error> {
unimplemented!() unimplemented!()
} }
fn kill(&self, pid: Pid) -> Result<(), nix::Error> { fn kill(&self, _pid: Pid) -> Result<(), nix::Error> {
unimplemented!() unimplemented!()
} }
fn force_kill(&self, pid: Pid) -> Result<(), nix::Error> { fn force_kill(&self, _pid: Pid) -> Result<(), nix::Error> {
unimplemented!() unimplemented!()
} }
fn box_clone(&self) -> Box<dyn ServerOsApi> { fn box_clone(&self) -> Box<dyn ServerOsApi> {
Box::new((*self).clone()) Box::new((*self).clone())
} }
fn send_to_client(&self, client_id: ClientId, msg: ServerToClientMsg) { fn send_to_client(&self, _client_id: ClientId, _msg: ServerToClientMsg) {
unimplemented!() unimplemented!()
} }
fn new_client( fn new_client(
&mut self, &mut self,
client_id: ClientId, _client_id: ClientId,
stream: LocalSocketStream, _stream: LocalSocketStream,
) -> IpcReceiverWithContext<ClientToServerMsg> { ) -> IpcReceiverWithContext<ClientToServerMsg> {
unimplemented!() unimplemented!()
} }
fn remove_client(&mut self, client_id: ClientId) { fn remove_client(&mut self, _client_id: ClientId) {
unimplemented!() unimplemented!()
} }
fn load_palette(&self) -> Palette { fn load_palette(&self) -> Palette {
@ -84,7 +83,8 @@ fn create_new_tab(size: Size) -> Tab {
let max_panes = None; let max_panes = None;
let mode_info = ModeInfo::default(); let mode_info = ModeInfo::default();
let colors = Palette::default(); let colors = Palette::default();
let session_state = Arc::new(RwLock::new(SessionState::new())); let draw_pane_frames = true;
let client_id = 1;
let mut tab = Tab::new( let mut tab = Tab::new(
index, index,
position, position,
@ -95,8 +95,8 @@ fn create_new_tab(size: Size) -> Tab {
max_panes, max_panes,
mode_info, mode_info,
colors, colors,
session_state, draw_pane_frames,
true, // draw pane frames client_id,
); );
tab.apply_layout( tab.apply_layout(
LayoutTemplate::default().try_into().unwrap(), LayoutTemplate::default().try_into().unwrap(),

View file

@ -244,7 +244,6 @@ pub enum ScreenContext {
SetFixedHeight, SetFixedHeight,
SetFixedWidth, SetFixedWidth,
ClosePane, ClosePane,
ApplyLayout,
NewTab, NewTab,
SwitchTabNext, SwitchTabNext,
SwitchTabPrev, SwitchTabPrev,
@ -258,6 +257,8 @@ pub enum ScreenContext {
MouseHold, MouseHold,
Copy, Copy,
ToggleTab, ToggleTab,
AddClient,
RemoveClient,
} }
/// Stack call representations corresponding to the different types of [`PtyInstruction`]s. /// Stack call representations corresponding to the different types of [`PtyInstruction`]s.