mirror of
https://github.com/zellij-org/zellij.git
synced 2024-11-22 22:26:54 +03:00
refactor(clients): support multiple clients in tab/screen rendering infra (#770)
This commit is contained in:
parent
24154e40e0
commit
f2401d0b25
@ -27,6 +27,7 @@ use crate::{
|
||||
os_input_output::ServerOsApi,
|
||||
pty::{pty_thread_main, Pty, PtyInstruction},
|
||||
screen::{screen_thread_main, ScreenInstruction},
|
||||
tab::Output,
|
||||
thread_bus::{Bus, ThreadSenders},
|
||||
wasm_vm::{wasm_thread_main, PluginInstruction},
|
||||
};
|
||||
@ -59,7 +60,7 @@ pub(crate) enum ServerInstruction {
|
||||
ClientId,
|
||||
Option<PluginsConfig>,
|
||||
),
|
||||
Render(Option<String>),
|
||||
Render(Option<Output>),
|
||||
UnblockInputThread,
|
||||
ClientExit(ClientId),
|
||||
RemoveClient(ClientId),
|
||||
@ -261,7 +262,6 @@ pub fn start_server(mut os_input: Box<dyn ServerOsApi>, socket_path: PathBuf) {
|
||||
os_input.clone(),
|
||||
to_server.clone(),
|
||||
client_attributes,
|
||||
session_state.clone(),
|
||||
SessionOptions {
|
||||
opts,
|
||||
layout: layout.clone(),
|
||||
@ -289,7 +289,11 @@ pub fn start_server(mut os_input: Box<dyn ServerOsApi>, socket_path: PathBuf) {
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.senders
|
||||
.send_to_pty(PtyInstruction::NewTab(default_shell.clone(), tab_layout))
|
||||
.send_to_pty(PtyInstruction::NewTab(
|
||||
default_shell.clone(),
|
||||
tab_layout,
|
||||
client_id,
|
||||
))
|
||||
.unwrap()
|
||||
};
|
||||
|
||||
@ -317,6 +321,10 @@ pub fn start_server(mut os_input: Box<dyn ServerOsApi>, socket_path: PathBuf) {
|
||||
.senders
|
||||
.send_to_screen(ScreenInstruction::TerminalResize(min_size))
|
||||
.unwrap();
|
||||
session_data
|
||||
.senders
|
||||
.send_to_screen(ScreenInstruction::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);
|
||||
@ -353,6 +361,16 @@ pub fn start_server(mut os_input: Box<dyn ServerOsApi>, socket_path: PathBuf) {
|
||||
.senders
|
||||
.send_to_screen(ScreenInstruction::TerminalResize(min_size))
|
||||
.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() {
|
||||
*session_data.write().unwrap() = None;
|
||||
@ -370,6 +388,16 @@ pub fn start_server(mut os_input: Box<dyn ServerOsApi>, socket_path: PathBuf) {
|
||||
.senders
|
||||
.send_to_screen(ScreenInstruction::TerminalResize(min_size))
|
||||
.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) => {
|
||||
@ -384,6 +412,16 @@ pub fn start_server(mut os_input: Box<dyn ServerOsApi>, socket_path: PathBuf) {
|
||||
.senders
|
||||
.send_to_screen(ScreenInstruction::TerminalResize(min_size))
|
||||
.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) => {
|
||||
@ -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 `None`- Send an exit instruction. This is the case when a user closes the last Tab/Pane.
|
||||
if let Some(op) = output.as_mut() {
|
||||
for client_id in client_ids {
|
||||
os_input.send_to_client(client_id, ServerToClientMsg::Render(op.clone()));
|
||||
for (client_id, client_render_instruction) in
|
||||
op.client_render_instructions.iter_mut()
|
||||
{
|
||||
os_input.send_to_client(
|
||||
*client_id,
|
||||
ServerToClientMsg::Render(client_render_instruction.clone()),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
for client_id in client_ids {
|
||||
@ -440,7 +483,6 @@ fn init_session(
|
||||
os_input: Box<dyn ServerOsApi>,
|
||||
to_server: SenderWithContext<ServerInstruction>,
|
||||
client_attributes: ClientAttributes,
|
||||
session_state: Arc<RwLock<SessionState>>,
|
||||
options: SessionOptions,
|
||||
) -> SessionMetaData {
|
||||
let SessionOptions {
|
||||
@ -508,13 +550,7 @@ fn init_session(
|
||||
let max_panes = opts.max_panes;
|
||||
|
||||
move || {
|
||||
screen_thread_main(
|
||||
screen_bus,
|
||||
max_panes,
|
||||
client_attributes,
|
||||
config_options,
|
||||
session_state,
|
||||
);
|
||||
screen_thread_main(screen_bus, max_panes, client_attributes, config_options);
|
||||
}
|
||||
})
|
||||
.unwrap();
|
||||
|
@ -4,7 +4,7 @@ use crate::{
|
||||
screen::ScreenInstruction,
|
||||
thread_bus::{Bus, ThreadSenders},
|
||||
wasm_vm::PluginInstruction,
|
||||
ServerInstruction,
|
||||
ClientId, ServerInstruction,
|
||||
};
|
||||
use async_std::{
|
||||
future::timeout as async_timeout,
|
||||
@ -36,7 +36,7 @@ pub(crate) enum PtyInstruction {
|
||||
SpawnTerminalVertically(Option<TerminalAction>),
|
||||
SpawnTerminalHorizontally(Option<TerminalAction>),
|
||||
UpdateActivePane(Option<PaneId>),
|
||||
NewTab(Option<TerminalAction>, Option<TabLayout>),
|
||||
NewTab(Option<TerminalAction>, Option<TabLayout>, ClientId),
|
||||
ClosePane(PaneId),
|
||||
CloseTab(Vec<PaneId>),
|
||||
Exit,
|
||||
@ -96,7 +96,7 @@ pub(crate) fn pty_thread_main(mut pty: Pty, layout: LayoutFromYaml) {
|
||||
PtyInstruction::UpdateActivePane(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| {
|
||||
if layout.name.is_empty() {
|
||||
None
|
||||
@ -109,7 +109,7 @@ pub(crate) fn pty_thread_main(mut pty: Pty, layout: LayoutFromYaml) {
|
||||
let layout: Layout =
|
||||
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 {
|
||||
// clear current name at first
|
||||
@ -284,6 +284,7 @@ impl Pty {
|
||||
&mut self,
|
||||
layout: Layout,
|
||||
default_shell: Option<TerminalAction>,
|
||||
client_id: ClientId,
|
||||
) {
|
||||
let default_shell = default_shell.unwrap_or_else(|| self.get_default_terminal());
|
||||
let extracted_run_instructions = layout.extract_run_instructions();
|
||||
@ -313,9 +314,10 @@ impl Pty {
|
||||
}
|
||||
self.bus
|
||||
.senders
|
||||
.send_to_screen(ScreenInstruction::ApplyLayout(
|
||||
.send_to_screen(ScreenInstruction::NewTab(
|
||||
layout,
|
||||
new_pane_pids.clone(),
|
||||
client_id,
|
||||
))
|
||||
.unwrap();
|
||||
for id in new_pane_pids {
|
||||
|
@ -204,7 +204,7 @@ fn route_action(
|
||||
let shell = session.default_shell.clone();
|
||||
session
|
||||
.senders
|
||||
.send_to_pty(PtyInstruction::NewTab(shell, tab_layout))
|
||||
.send_to_pty(PtyInstruction::NewTab(shell, tab_layout, client_id))
|
||||
.unwrap();
|
||||
}
|
||||
Action::GoToNextTab => {
|
||||
|
@ -3,7 +3,6 @@
|
||||
use std::collections::BTreeMap;
|
||||
use std::os::unix::io::RawFd;
|
||||
use std::str;
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
use zellij_utils::pane_size::Size;
|
||||
use zellij_utils::{input::layout::Layout, position::Position, zellij_tile};
|
||||
@ -14,7 +13,7 @@ use crate::{
|
||||
tab::Tab,
|
||||
thread_bus::Bus,
|
||||
wasm_vm::PluginInstruction,
|
||||
ServerInstruction, SessionState,
|
||||
ClientId, ServerInstruction,
|
||||
};
|
||||
use zellij_tile::data::{Event, InputMode, ModeInfo, Palette, PluginCapabilities, TabInfo};
|
||||
use zellij_utils::{
|
||||
@ -59,7 +58,7 @@ pub(crate) enum ScreenInstruction {
|
||||
TogglePaneFrames,
|
||||
SetSelectable(PaneId, bool, usize),
|
||||
ClosePane(PaneId),
|
||||
ApplyLayout(Layout, Vec<RawFd>),
|
||||
NewTab(Layout, Vec<RawFd>, ClientId),
|
||||
SwitchTabNext,
|
||||
SwitchTabPrev,
|
||||
ToggleActiveSyncTab,
|
||||
@ -73,6 +72,8 @@ pub(crate) enum ScreenInstruction {
|
||||
MouseRelease(Position),
|
||||
MouseHold(Position),
|
||||
Copy,
|
||||
AddClient(ClientId),
|
||||
RemoveClient(ClientId),
|
||||
}
|
||||
|
||||
impl From<&ScreenInstruction> for ScreenContext {
|
||||
@ -113,7 +114,7 @@ impl From<&ScreenInstruction> for ScreenContext {
|
||||
ScreenInstruction::TogglePaneFrames => ScreenContext::TogglePaneFrames,
|
||||
ScreenInstruction::SetSelectable(..) => ScreenContext::SetSelectable,
|
||||
ScreenInstruction::ClosePane(_) => ScreenContext::ClosePane,
|
||||
ScreenInstruction::ApplyLayout(..) => ScreenContext::ApplyLayout,
|
||||
ScreenInstruction::NewTab(..) => ScreenContext::NewTab,
|
||||
ScreenInstruction::SwitchTabNext => ScreenContext::SwitchTabNext,
|
||||
ScreenInstruction::SwitchTabPrev => ScreenContext::SwitchTabPrev,
|
||||
ScreenInstruction::CloseTab => ScreenContext::CloseTab,
|
||||
@ -129,6 +130,8 @@ impl From<&ScreenInstruction> for ScreenContext {
|
||||
ScreenInstruction::MouseHold(_) => ScreenContext::MouseHold,
|
||||
ScreenInstruction::Copy => ScreenContext::Copy,
|
||||
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>>,
|
||||
mode_info: ModeInfo,
|
||||
colors: Palette,
|
||||
session_state: Arc<RwLock<SessionState>>,
|
||||
draw_pane_frames: bool,
|
||||
}
|
||||
|
||||
@ -160,7 +162,6 @@ impl Screen {
|
||||
client_attributes: &ClientAttributes,
|
||||
max_panes: Option<usize>,
|
||||
mode_info: ModeInfo,
|
||||
session_state: Arc<RwLock<SessionState>>,
|
||||
draw_pane_frames: bool,
|
||||
) -> Self {
|
||||
Screen {
|
||||
@ -172,7 +173,6 @@ impl Screen {
|
||||
tabs: BTreeMap::new(),
|
||||
tab_history: Vec::with_capacity(32),
|
||||
mode_info,
|
||||
session_state,
|
||||
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.
|
||||
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) {
|
||||
@ -199,6 +207,7 @@ impl Screen {
|
||||
}
|
||||
|
||||
current_tab.visible(false);
|
||||
let current_tab_index = current_tab.index;
|
||||
let new_tab_index = new_tab.index;
|
||||
let new_tab = self.get_indexed_tab_mut(new_tab_index).unwrap();
|
||||
new_tab.set_force_render();
|
||||
@ -214,6 +223,8 @@ impl Screen {
|
||||
self.tab_history.retain(|&e| e != Some(new_tab_pos));
|
||||
self.tab_history.push(old_active_index);
|
||||
|
||||
self.move_clients(current_tab_index, new_tab_index);
|
||||
|
||||
self.update_tabs();
|
||||
self.render();
|
||||
}
|
||||
@ -295,7 +306,12 @@ impl Screen {
|
||||
pub fn render(&mut self) {
|
||||
if let Some(active_tab) = self.get_active_tab_mut() {
|
||||
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 {
|
||||
self.close_tab();
|
||||
}
|
||||
@ -341,7 +357,7 @@ impl Screen {
|
||||
|
||||
/// Creates a new [`Tab`] in this [`Screen`], applying the specified [`Layout`]
|
||||
/// 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 position = self.tabs.len();
|
||||
let mut tab = Tab::new(
|
||||
@ -354,19 +370,30 @@ impl Screen {
|
||||
self.max_panes,
|
||||
self.mode_info.clone(),
|
||||
self.colors,
|
||||
self.session_state.clone(),
|
||||
self.draw_pane_frames,
|
||||
client_id,
|
||||
);
|
||||
tab.apply_layout(layout, new_pids, tab_index);
|
||||
if let Some(active_tab) = self.get_active_tab_mut() {
|
||||
active_tab.visible(false);
|
||||
active_tab.is_active = false;
|
||||
let connected_clients = active_tab.drain_connected_clients();
|
||||
tab.add_multiple_clients(&connected_clients);
|
||||
}
|
||||
self.tab_history
|
||||
.push(self.active_tab_index.replace(tab_index));
|
||||
tab.visible(true);
|
||||
self.tabs.insert(tab_index, tab);
|
||||
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) {
|
||||
@ -450,7 +477,6 @@ pub(crate) fn screen_thread_main(
|
||||
max_panes: Option<usize>,
|
||||
client_attributes: ClientAttributes,
|
||||
config_options: Box<Options>,
|
||||
session_state: Arc<RwLock<SessionState>>,
|
||||
) {
|
||||
let capabilities = config_options.simplified_ui;
|
||||
let draw_pane_frames = !config_options.no_pane_frames;
|
||||
@ -466,7 +492,6 @@ pub(crate) fn screen_thread_main(
|
||||
arrow_fonts: capabilities,
|
||||
},
|
||||
),
|
||||
session_state,
|
||||
draw_pane_frames,
|
||||
);
|
||||
loop {
|
||||
@ -505,6 +530,8 @@ pub(crate) fn screen_thread_main(
|
||||
.send_to_server(ServerInstruction::UnblockInputThread)
|
||||
.unwrap();
|
||||
screen.update_tabs();
|
||||
|
||||
screen.render();
|
||||
}
|
||||
ScreenInstruction::HorizontalSplit(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)
|
||||
.unwrap();
|
||||
screen.update_tabs();
|
||||
|
||||
screen.render();
|
||||
}
|
||||
ScreenInstruction::VerticalSplit(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)
|
||||
.unwrap();
|
||||
screen.update_tabs();
|
||||
|
||||
screen.render();
|
||||
}
|
||||
ScreenInstruction::WriteCharacter(bytes) => {
|
||||
let active_tab = screen.get_active_tab_mut().unwrap();
|
||||
@ -533,27 +564,43 @@ pub(crate) fn screen_thread_main(
|
||||
}
|
||||
ScreenInstruction::ResizeLeft => {
|
||||
screen.get_active_tab_mut().unwrap().resize_left();
|
||||
|
||||
screen.render();
|
||||
}
|
||||
ScreenInstruction::ResizeRight => {
|
||||
screen.get_active_tab_mut().unwrap().resize_right();
|
||||
|
||||
screen.render();
|
||||
}
|
||||
ScreenInstruction::ResizeDown => {
|
||||
screen.get_active_tab_mut().unwrap().resize_down();
|
||||
|
||||
screen.render();
|
||||
}
|
||||
ScreenInstruction::ResizeUp => {
|
||||
screen.get_active_tab_mut().unwrap().resize_up();
|
||||
|
||||
screen.render();
|
||||
}
|
||||
ScreenInstruction::SwitchFocus => {
|
||||
screen.get_active_tab_mut().unwrap().move_focus();
|
||||
|
||||
screen.render();
|
||||
}
|
||||
ScreenInstruction::FocusNextPane => {
|
||||
screen.get_active_tab_mut().unwrap().focus_next_pane();
|
||||
|
||||
screen.render();
|
||||
}
|
||||
ScreenInstruction::FocusPreviousPane => {
|
||||
screen.get_active_tab_mut().unwrap().focus_previous_pane();
|
||||
|
||||
screen.render();
|
||||
}
|
||||
ScreenInstruction::MoveFocusLeft => {
|
||||
screen.get_active_tab_mut().unwrap().move_focus_left();
|
||||
|
||||
screen.render();
|
||||
}
|
||||
ScreenInstruction::MoveFocusLeftOrPreviousTab => {
|
||||
screen.move_focus_left_or_previous_tab();
|
||||
@ -562,12 +609,18 @@ pub(crate) fn screen_thread_main(
|
||||
.senders
|
||||
.send_to_server(ServerInstruction::UnblockInputThread)
|
||||
.unwrap();
|
||||
|
||||
screen.render();
|
||||
}
|
||||
ScreenInstruction::MoveFocusDown => {
|
||||
screen.get_active_tab_mut().unwrap().move_focus_down();
|
||||
|
||||
screen.render();
|
||||
}
|
||||
ScreenInstruction::MoveFocusRight => {
|
||||
screen.get_active_tab_mut().unwrap().move_focus_right();
|
||||
|
||||
screen.render();
|
||||
}
|
||||
ScreenInstruction::MoveFocusRightOrNextTab => {
|
||||
screen.move_focus_right_or_next_tab();
|
||||
@ -576,61 +629,81 @@ pub(crate) fn screen_thread_main(
|
||||
.senders
|
||||
.send_to_server(ServerInstruction::UnblockInputThread)
|
||||
.unwrap();
|
||||
|
||||
screen.render();
|
||||
}
|
||||
ScreenInstruction::MoveFocusUp => {
|
||||
screen.get_active_tab_mut().unwrap().move_focus_up();
|
||||
|
||||
screen.render();
|
||||
}
|
||||
ScreenInstruction::ScrollUp => {
|
||||
screen
|
||||
.get_active_tab_mut()
|
||||
.unwrap()
|
||||
.scroll_active_terminal_up();
|
||||
|
||||
screen.render();
|
||||
}
|
||||
ScreenInstruction::ScrollUpAt(point) => {
|
||||
screen
|
||||
.get_active_tab_mut()
|
||||
.unwrap()
|
||||
.scroll_terminal_up(&point, 3);
|
||||
|
||||
screen.render();
|
||||
}
|
||||
ScreenInstruction::ScrollDown => {
|
||||
screen
|
||||
.get_active_tab_mut()
|
||||
.unwrap()
|
||||
.scroll_active_terminal_down();
|
||||
|
||||
screen.render();
|
||||
}
|
||||
ScreenInstruction::ScrollDownAt(point) => {
|
||||
screen
|
||||
.get_active_tab_mut()
|
||||
.unwrap()
|
||||
.scroll_terminal_down(&point, 3);
|
||||
|
||||
screen.render();
|
||||
}
|
||||
ScreenInstruction::ScrollToBottom => {
|
||||
screen
|
||||
.get_active_tab_mut()
|
||||
.unwrap()
|
||||
.scroll_active_terminal_to_bottom();
|
||||
|
||||
screen.render();
|
||||
}
|
||||
ScreenInstruction::PageScrollUp => {
|
||||
screen
|
||||
.get_active_tab_mut()
|
||||
.unwrap()
|
||||
.scroll_active_terminal_up_page();
|
||||
|
||||
screen.render();
|
||||
}
|
||||
ScreenInstruction::PageScrollDown => {
|
||||
screen
|
||||
.get_active_tab_mut()
|
||||
.unwrap()
|
||||
.scroll_active_terminal_down_page();
|
||||
|
||||
screen.render();
|
||||
}
|
||||
ScreenInstruction::ClearScroll => {
|
||||
screen
|
||||
.get_active_tab_mut()
|
||||
.unwrap()
|
||||
.clear_active_terminal_scroll();
|
||||
|
||||
screen.render();
|
||||
}
|
||||
ScreenInstruction::CloseFocusedPane => {
|
||||
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) => {
|
||||
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),
|
||||
);
|
||||
|
||||
screen.render();
|
||||
}
|
||||
ScreenInstruction::ClosePane(id) => {
|
||||
screen.get_active_tab_mut().unwrap().close_pane(id);
|
||||
@ -654,6 +729,8 @@ pub(crate) fn screen_thread_main(
|
||||
.unwrap()
|
||||
.toggle_active_pane_fullscreen();
|
||||
screen.update_tabs();
|
||||
|
||||
screen.render();
|
||||
}
|
||||
ScreenInstruction::TogglePaneFrames => {
|
||||
screen.draw_pane_frames = !screen.draw_pane_frames;
|
||||
@ -669,6 +746,8 @@ pub(crate) fn screen_thread_main(
|
||||
.senders
|
||||
.send_to_server(ServerInstruction::UnblockInputThread)
|
||||
.unwrap();
|
||||
|
||||
screen.render();
|
||||
}
|
||||
ScreenInstruction::SwitchTabPrev => {
|
||||
screen.switch_tab_prev();
|
||||
@ -677,6 +756,8 @@ pub(crate) fn screen_thread_main(
|
||||
.senders
|
||||
.send_to_server(ServerInstruction::UnblockInputThread)
|
||||
.unwrap();
|
||||
|
||||
screen.render();
|
||||
}
|
||||
ScreenInstruction::CloseTab => {
|
||||
screen.close_tab();
|
||||
@ -685,14 +766,18 @@ pub(crate) fn screen_thread_main(
|
||||
.senders
|
||||
.send_to_server(ServerInstruction::UnblockInputThread)
|
||||
.unwrap();
|
||||
|
||||
screen.render();
|
||||
}
|
||||
ScreenInstruction::ApplyLayout(layout, new_pane_pids) => {
|
||||
screen.apply_layout(layout, new_pane_pids);
|
||||
ScreenInstruction::NewTab(layout, new_pane_pids, client_id) => {
|
||||
screen.new_tab(layout, new_pane_pids, client_id);
|
||||
screen
|
||||
.bus
|
||||
.senders
|
||||
.send_to_server(ServerInstruction::UnblockInputThread)
|
||||
.unwrap();
|
||||
|
||||
screen.render();
|
||||
}
|
||||
ScreenInstruction::GoToTab(tab_index) => {
|
||||
screen.go_to_tab(tab_index as usize);
|
||||
@ -701,15 +786,23 @@ pub(crate) fn screen_thread_main(
|
||||
.senders
|
||||
.send_to_server(ServerInstruction::UnblockInputThread)
|
||||
.unwrap();
|
||||
|
||||
screen.render();
|
||||
}
|
||||
ScreenInstruction::UpdateTabName(c) => {
|
||||
screen.update_active_tab_name(c);
|
||||
|
||||
screen.render();
|
||||
}
|
||||
ScreenInstruction::TerminalResize(new_size) => {
|
||||
screen.resize_to_screen(new_size);
|
||||
|
||||
screen.render();
|
||||
}
|
||||
ScreenInstruction::ChangeMode(mode_info) => {
|
||||
screen.change_mode(mode_info);
|
||||
|
||||
screen.render();
|
||||
}
|
||||
ScreenInstruction::ToggleActiveSyncTab => {
|
||||
screen
|
||||
@ -717,27 +810,37 @@ pub(crate) fn screen_thread_main(
|
||||
.unwrap()
|
||||
.toggle_sync_panes_is_active();
|
||||
screen.update_tabs();
|
||||
|
||||
screen.render();
|
||||
}
|
||||
ScreenInstruction::LeftClick(point) => {
|
||||
screen
|
||||
.get_active_tab_mut()
|
||||
.unwrap()
|
||||
.handle_left_click(&point);
|
||||
|
||||
screen.render();
|
||||
}
|
||||
ScreenInstruction::MouseRelease(point) => {
|
||||
screen
|
||||
.get_active_tab_mut()
|
||||
.unwrap()
|
||||
.handle_mouse_release(&point);
|
||||
|
||||
screen.render();
|
||||
}
|
||||
ScreenInstruction::MouseHold(point) => {
|
||||
screen
|
||||
.get_active_tab_mut()
|
||||
.unwrap()
|
||||
.handle_mouse_hold(&point);
|
||||
|
||||
screen.render();
|
||||
}
|
||||
ScreenInstruction::Copy => {
|
||||
screen.get_active_tab().unwrap().copy_selection();
|
||||
|
||||
screen.render();
|
||||
}
|
||||
ScreenInstruction::Exit => {
|
||||
break;
|
||||
@ -749,6 +852,18 @@ pub(crate) fn screen_thread_main(
|
||||
.senders
|
||||
.send_to_server(ServerInstruction::UnblockInputThread)
|
||||
.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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,11 +8,11 @@ use crate::{
|
||||
thread_bus::ThreadSenders,
|
||||
ui::boundaries::Boundaries,
|
||||
wasm_vm::PluginInstruction,
|
||||
ServerInstruction, SessionState,
|
||||
ClientId, ServerInstruction,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::os::unix::io::RawFd;
|
||||
use std::sync::{mpsc::channel, Arc, RwLock};
|
||||
use std::sync::mpsc::channel;
|
||||
use std::time::Instant;
|
||||
use std::{
|
||||
cmp::Reverse,
|
||||
@ -40,7 +40,6 @@ const MIN_TERMINAL_WIDTH: usize = 5;
|
||||
const RESIZE_PERCENT: f64 = 5.0;
|
||||
|
||||
const MAX_PENDING_VTE_EVENTS: usize = 7000;
|
||||
const PENDING_EVENTS_SET_SIZE: usize = 200;
|
||||
|
||||
type BorderAndPaneIds = (usize, Vec<PaneId>);
|
||||
|
||||
@ -97,6 +96,28 @@ fn pane_content_offset(position_and_size: &PaneGeom, viewport: &Viewport) -> (us
|
||||
(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 index: usize,
|
||||
pub position: usize,
|
||||
@ -112,10 +133,10 @@ pub(crate) struct Tab {
|
||||
pub senders: ThreadSenders,
|
||||
synchronize_is_active: bool,
|
||||
should_clear_display_before_rendering: bool,
|
||||
session_state: Arc<RwLock<SessionState>>,
|
||||
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>>,
|
||||
}
|
||||
@ -268,8 +289,8 @@ impl Tab {
|
||||
max_panes: Option<usize>,
|
||||
mode_info: ModeInfo,
|
||||
colors: Palette,
|
||||
session_state: Arc<RwLock<SessionState>>,
|
||||
draw_pane_frames: bool,
|
||||
client_id: ClientId,
|
||||
) -> Self {
|
||||
let panes = BTreeMap::new();
|
||||
|
||||
@ -279,6 +300,9 @@ impl Tab {
|
||||
name
|
||||
};
|
||||
|
||||
let mut connected_clients = HashSet::new();
|
||||
connected_clients.insert(client_id);
|
||||
|
||||
Tab {
|
||||
index,
|
||||
position,
|
||||
@ -296,10 +320,10 @@ impl Tab {
|
||||
should_clear_display_before_rendering: false,
|
||||
mode_info,
|
||||
colors,
|
||||
session_state,
|
||||
draw_pane_frames,
|
||||
pending_vte_events: HashMap::new(),
|
||||
is_active: true,
|
||||
connected_clients,
|
||||
}
|
||||
}
|
||||
|
||||
@ -397,7 +421,23 @@ impl Tab {
|
||||
// This is the end of the nasty viewport hack...
|
||||
// FIXME: Active / new / current terminal, should be pane
|
||||
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) {
|
||||
self.close_down_to_max_terminals();
|
||||
@ -470,7 +510,6 @@ impl Tab {
|
||||
}
|
||||
}
|
||||
self.active_terminal = Some(pid);
|
||||
self.render();
|
||||
}
|
||||
pub fn horizontal_split(&mut self, pid: PaneId) {
|
||||
self.close_down_to_max_terminals();
|
||||
@ -500,7 +539,6 @@ impl Tab {
|
||||
self.panes.insert(pid, Box::new(new_terminal));
|
||||
self.active_terminal = Some(pid);
|
||||
self.relayout_tab(Direction::Vertical);
|
||||
self.render();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -529,7 +567,6 @@ impl Tab {
|
||||
}
|
||||
self.active_terminal = Some(pid);
|
||||
self.relayout_tab(Direction::Horizontal);
|
||||
self.render();
|
||||
}
|
||||
}
|
||||
pub fn get_active_pane(&self) -> Option<&dyn Pane> {
|
||||
@ -555,10 +592,10 @@ impl Tab {
|
||||
if terminal_output.is_scrolled() {
|
||||
self.pending_vte_events.entry(pid).or_default().push(bytes);
|
||||
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 {
|
||||
terminal_output.clear_scroll();
|
||||
self.play_pending_vte_events(pid);
|
||||
self.process_pending_vte_events(pid);
|
||||
}
|
||||
}
|
||||
return;
|
||||
@ -566,25 +603,12 @@ impl Tab {
|
||||
}
|
||||
self.process_pty_bytes(pid, bytes);
|
||||
}
|
||||
fn play_pending_vte_events(&mut self, pid: RawFd) {
|
||||
if self.pending_vte_events.get(&pid).is_some() {
|
||||
if let Some(terminal_output) = self.panes.get_mut(&PaneId::Terminal(pid)) {
|
||||
if terminal_output.is_scrolled() {
|
||||
return;
|
||||
}
|
||||
pub fn process_pending_vte_events(&mut self, pid: RawFd) {
|
||||
if let Some(pending_vte_events) = self.pending_vte_events.get_mut(&pid) {
|
||||
let vte_events: Vec<VteBytes> = pending_vte_events.drain(..).collect();
|
||||
for vte_event in vte_events {
|
||||
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) {
|
||||
@ -699,7 +723,6 @@ impl Tab {
|
||||
}
|
||||
self.set_force_render();
|
||||
self.resize_whole_tab(self.display_area);
|
||||
self.render();
|
||||
self.toggle_fullscreen_is_active();
|
||||
}
|
||||
}
|
||||
@ -772,26 +795,20 @@ impl Tab {
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn render(&mut self) {
|
||||
if self.active_terminal.is_none()
|
||||
|| self.session_state.read().unwrap().clients.is_empty()
|
||||
|| !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;
|
||||
pub fn render(&mut self) -> Option<Output> {
|
||||
if self.connected_clients.is_empty() || self.active_terminal.is_none() {
|
||||
return None;
|
||||
}
|
||||
self.senders
|
||||
.send_to_pty(PtyInstruction::UpdateActivePane(self.active_terminal))
|
||||
.unwrap();
|
||||
let mut output = String::new();
|
||||
let mut output = Output::new(&self.connected_clients);
|
||||
let mut boundaries = Boundaries::new(self.viewport);
|
||||
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 {
|
||||
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;
|
||||
}
|
||||
for (_kind, pane) in self.panes.iter_mut() {
|
||||
@ -824,7 +841,7 @@ impl Tab {
|
||||
}
|
||||
if let Some(vte_output) = pane.render() {
|
||||
// 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{}",
|
||||
pane.y() + 1,
|
||||
pane.x() + 1,
|
||||
@ -835,7 +852,7 @@ impl Tab {
|
||||
}
|
||||
|
||||
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() {
|
||||
@ -848,18 +865,15 @@ impl Tab {
|
||||
cursor_position_x + 1,
|
||||
change_cursor_shape
|
||||
); // goto row/col
|
||||
output.push_str(show_cursor);
|
||||
output.push_str(goto_cursor_position);
|
||||
output.push_str_to_all_clients(show_cursor);
|
||||
output.push_str_to_all_clients(goto_cursor_position);
|
||||
}
|
||||
None => {
|
||||
let hide_cursor = "\u{1b}[?25l";
|
||||
output.push_str(hide_cursor);
|
||||
output.push_str_to_all_clients(hide_cursor);
|
||||
}
|
||||
}
|
||||
|
||||
self.senders
|
||||
.send_to_server(ServerInstruction::Render(Some(output)))
|
||||
.unwrap();
|
||||
Some(output)
|
||||
}
|
||||
fn get_panes(&self) -> impl Iterator<Item = (&PaneId, &Box<dyn Pane>)> {
|
||||
self.panes.iter()
|
||||
@ -1757,7 +1771,6 @@ impl Tab {
|
||||
}
|
||||
}
|
||||
self.relayout_tab(Direction::Horizontal);
|
||||
self.render();
|
||||
}
|
||||
pub fn resize_right(&mut self) {
|
||||
// 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.render();
|
||||
}
|
||||
pub fn resize_down(&mut self) {
|
||||
// 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.render();
|
||||
}
|
||||
pub fn resize_up(&mut self) {
|
||||
// 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.render();
|
||||
}
|
||||
pub fn move_focus(&mut self) {
|
||||
if !self.has_selectable_panes() {
|
||||
@ -1814,7 +1824,6 @@ impl Tab {
|
||||
.copied();
|
||||
|
||||
self.active_terminal = active_terminal;
|
||||
self.render();
|
||||
}
|
||||
pub fn focus_next_pane(&mut self) {
|
||||
if !self.has_selectable_panes() {
|
||||
@ -1843,7 +1852,6 @@ impl Tab {
|
||||
.map(|p| *p.0);
|
||||
|
||||
self.active_terminal = active_terminal;
|
||||
self.render();
|
||||
}
|
||||
pub fn focus_previous_pane(&mut self) {
|
||||
if !self.has_selectable_panes() {
|
||||
@ -1873,7 +1881,6 @@ impl Tab {
|
||||
Some(*panes.get(active_pane_position - 1).unwrap().0)
|
||||
};
|
||||
self.active_terminal = active_terminal;
|
||||
self.render();
|
||||
}
|
||||
// returns a boolean that indicates whether the focus moved
|
||||
pub fn move_focus_left(&mut self) -> bool {
|
||||
@ -1904,7 +1911,6 @@ impl Tab {
|
||||
next_active_pane.set_should_render(true);
|
||||
|
||||
self.active_terminal = Some(p);
|
||||
self.render();
|
||||
return true;
|
||||
}
|
||||
None => Some(active.pid()),
|
||||
@ -1950,7 +1956,6 @@ impl Tab {
|
||||
Some(active_terminal.unwrap().pid())
|
||||
};
|
||||
self.active_terminal = updated_active_terminal;
|
||||
self.render();
|
||||
}
|
||||
pub fn move_focus_up(&mut self) {
|
||||
if !self.has_selectable_panes() {
|
||||
@ -1987,7 +1992,6 @@ impl Tab {
|
||||
Some(active_terminal.unwrap().pid())
|
||||
};
|
||||
self.active_terminal = updated_active_terminal;
|
||||
self.render();
|
||||
}
|
||||
// returns a boolean that indicates whether the focus moved
|
||||
pub fn move_focus_right(&mut self) -> bool {
|
||||
@ -2018,7 +2022,6 @@ impl Tab {
|
||||
next_active_pane.set_should_render(true);
|
||||
|
||||
self.active_terminal = Some(p);
|
||||
self.render();
|
||||
return true;
|
||||
}
|
||||
None => Some(active.pid()),
|
||||
@ -2175,7 +2178,6 @@ impl Tab {
|
||||
self.active_terminal = self.next_active_pane(&self.get_pane_ids());
|
||||
}
|
||||
}
|
||||
self.render();
|
||||
}
|
||||
pub fn close_pane(&mut self, id: PaneId) {
|
||||
if self.fullscreen_is_active {
|
||||
@ -2256,7 +2258,6 @@ impl Tab {
|
||||
.get_mut(&PaneId::Terminal(active_terminal_id))
|
||||
.unwrap();
|
||||
active_terminal.scroll_up(1);
|
||||
self.render();
|
||||
}
|
||||
}
|
||||
pub fn scroll_active_terminal_down(&mut self) {
|
||||
@ -2266,8 +2267,9 @@ impl Tab {
|
||||
.get_mut(&PaneId::Terminal(active_terminal_id))
|
||||
.unwrap();
|
||||
active_terminal.scroll_down(1);
|
||||
self.play_pending_vte_events(active_terminal_id);
|
||||
self.render();
|
||||
if !active_terminal.is_scrolled() {
|
||||
self.process_pending_vte_events(active_terminal_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn scroll_active_terminal_up_page(&mut self) {
|
||||
@ -2279,7 +2281,6 @@ impl Tab {
|
||||
// prevent overflow when row == 0
|
||||
let scroll_columns = active_terminal.rows().max(1) - 1;
|
||||
active_terminal.scroll_up(scroll_columns);
|
||||
self.render();
|
||||
}
|
||||
}
|
||||
pub fn scroll_active_terminal_down_page(&mut self) {
|
||||
@ -2291,8 +2292,9 @@ impl Tab {
|
||||
// prevent overflow when row == 0
|
||||
let scroll_columns = active_terminal.rows().max(1) - 1;
|
||||
active_terminal.scroll_down(scroll_columns);
|
||||
self.play_pending_vte_events(active_terminal_id);
|
||||
self.render();
|
||||
if !active_terminal.is_scrolled() {
|
||||
self.process_pending_vte_events(active_terminal_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn scroll_active_terminal_to_bottom(&mut self) {
|
||||
@ -2302,8 +2304,9 @@ impl Tab {
|
||||
.get_mut(&PaneId::Terminal(active_terminal_id))
|
||||
.unwrap();
|
||||
active_terminal.clear_scroll();
|
||||
self.play_pending_vte_events(active_terminal_id);
|
||||
self.render();
|
||||
if !active_terminal.is_scrolled() {
|
||||
self.process_pending_vte_events(active_terminal_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn clear_active_terminal_scroll(&mut self) {
|
||||
@ -2313,22 +2316,24 @@ impl Tab {
|
||||
.get_mut(&PaneId::Terminal(active_terminal_id))
|
||||
.unwrap();
|
||||
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) {
|
||||
if let Some(pane) = self.get_pane_at(point) {
|
||||
pane.scroll_up(lines);
|
||||
self.render();
|
||||
}
|
||||
}
|
||||
pub fn scroll_terminal_down(&mut self, point: &Position, lines: usize) {
|
||||
if let Some(pane) = self.get_pane_at(point) {
|
||||
pane.scroll_down(lines);
|
||||
if let PaneId::Terminal(id) = pane.pid() {
|
||||
self.play_pending_vte_events(id);
|
||||
if !pane.is_scrolled() {
|
||||
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>> {
|
||||
@ -2353,13 +2358,11 @@ impl Tab {
|
||||
if let Some(pane) = self.get_pane_at(position) {
|
||||
let relative_position = pane.relative_position(position);
|
||||
pane.start_selection(&relative_position);
|
||||
self.render();
|
||||
};
|
||||
}
|
||||
fn focus_pane_at(&mut self, point: &Position) {
|
||||
if let Some(clicked_pane) = self.get_pane_id_at(point) {
|
||||
self.active_terminal = Some(clicked_pane);
|
||||
self.render();
|
||||
}
|
||||
}
|
||||
pub fn handle_mouse_release(&mut self, position: &Position) {
|
||||
@ -2372,7 +2375,6 @@ impl Tab {
|
||||
active_pane.end_selection(None);
|
||||
selected_text = active_pane.get_selected_text();
|
||||
active_pane.reset_selection();
|
||||
self.render();
|
||||
}
|
||||
}
|
||||
} else if let Some(pane) = self.get_pane_at(position) {
|
||||
@ -2380,7 +2382,6 @@ impl Tab {
|
||||
pane.end_selection(Some(&relative_position));
|
||||
selected_text = pane.get_selected_text();
|
||||
pane.reset_selection();
|
||||
self.render();
|
||||
}
|
||||
|
||||
if let Some(selected_text) = selected_text {
|
||||
@ -2394,7 +2395,6 @@ impl Tab {
|
||||
active_pane.update_selection(&relative_position);
|
||||
}
|
||||
}
|
||||
self.render();
|
||||
}
|
||||
|
||||
pub fn copy_selection(&self) {
|
||||
@ -2408,7 +2408,13 @@ impl Tab {
|
||||
}
|
||||
|
||||
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
|
||||
.send_to_server(ServerInstruction::Render(Some(output)))
|
||||
.unwrap();
|
||||
|
@ -3,11 +3,10 @@ use crate::zellij_tile::data::{ModeInfo, Palette};
|
||||
use crate::{
|
||||
os_input_output::{AsyncReader, ChildId, Pid, ServerOsApi},
|
||||
thread_bus::Bus,
|
||||
ClientId, SessionState,
|
||||
ClientId,
|
||||
};
|
||||
use std::convert::TryInto;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::{Arc, RwLock};
|
||||
use zellij_utils::input::command::TerminalAction;
|
||||
use zellij_utils::input::layout::LayoutTemplate;
|
||||
use zellij_utils::ipc::IpcReceiverWithContext;
|
||||
@ -27,44 +26,44 @@ use zellij_utils::{
|
||||
struct 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
|
||||
}
|
||||
fn spawn_terminal(&self, _file_to_open: TerminalAction) -> (RawFd, ChildId) {
|
||||
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!()
|
||||
}
|
||||
fn async_file_reader(&self, fd: RawFd) -> Box<dyn AsyncReader> {
|
||||
fn async_file_reader(&self, _fd: RawFd) -> Box<dyn AsyncReader> {
|
||||
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!()
|
||||
}
|
||||
fn tcdrain(&self, fd: RawFd) -> Result<(), nix::Error> {
|
||||
fn tcdrain(&self, _fd: RawFd) -> Result<(), nix::Error> {
|
||||
unimplemented!()
|
||||
}
|
||||
fn kill(&self, pid: Pid) -> Result<(), nix::Error> {
|
||||
fn kill(&self, _pid: Pid) -> Result<(), nix::Error> {
|
||||
unimplemented!()
|
||||
}
|
||||
fn force_kill(&self, pid: Pid) -> Result<(), nix::Error> {
|
||||
fn force_kill(&self, _pid: Pid) -> Result<(), nix::Error> {
|
||||
unimplemented!()
|
||||
}
|
||||
fn box_clone(&self) -> Box<dyn ServerOsApi> {
|
||||
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!()
|
||||
}
|
||||
fn new_client(
|
||||
&mut self,
|
||||
client_id: ClientId,
|
||||
stream: LocalSocketStream,
|
||||
_client_id: ClientId,
|
||||
_stream: LocalSocketStream,
|
||||
) -> IpcReceiverWithContext<ClientToServerMsg> {
|
||||
unimplemented!()
|
||||
}
|
||||
fn remove_client(&mut self, client_id: ClientId) {
|
||||
fn remove_client(&mut self, _client_id: ClientId) {
|
||||
unimplemented!()
|
||||
}
|
||||
fn load_palette(&self) -> Palette {
|
||||
@ -85,19 +84,23 @@ fn create_new_screen(size: Size) -> Screen {
|
||||
};
|
||||
let max_panes = None;
|
||||
let mode_info = ModeInfo::default();
|
||||
let session_state = Arc::new(RwLock::new(SessionState::new()));
|
||||
let draw_pane_frames = false;
|
||||
Screen::new(
|
||||
bus,
|
||||
&client_attributes,
|
||||
max_panes,
|
||||
mode_info,
|
||||
session_state,
|
||||
false, // draw_pane_frames
|
||||
draw_pane_frames,
|
||||
)
|
||||
}
|
||||
|
||||
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]
|
||||
|
@ -4,11 +4,10 @@ use crate::{
|
||||
os_input_output::{AsyncReader, ChildId, Pid, ServerOsApi},
|
||||
panes::PaneId,
|
||||
thread_bus::ThreadSenders,
|
||||
ClientId, SessionState,
|
||||
ClientId,
|
||||
};
|
||||
use std::convert::TryInto;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::{Arc, RwLock};
|
||||
use zellij_utils::input::layout::LayoutTemplate;
|
||||
use zellij_utils::ipc::IpcReceiverWithContext;
|
||||
use zellij_utils::pane_size::Size;
|
||||
@ -27,44 +26,44 @@ use zellij_utils::{
|
||||
struct 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
|
||||
}
|
||||
fn spawn_terminal(&self, _file_to_open: TerminalAction) -> (RawFd, ChildId) {
|
||||
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!()
|
||||
}
|
||||
fn async_file_reader(&self, fd: RawFd) -> Box<dyn AsyncReader> {
|
||||
fn async_file_reader(&self, _fd: RawFd) -> Box<dyn AsyncReader> {
|
||||
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!()
|
||||
}
|
||||
fn tcdrain(&self, fd: RawFd) -> Result<(), nix::Error> {
|
||||
fn tcdrain(&self, _fd: RawFd) -> Result<(), nix::Error> {
|
||||
unimplemented!()
|
||||
}
|
||||
fn kill(&self, pid: Pid) -> Result<(), nix::Error> {
|
||||
fn kill(&self, _pid: Pid) -> Result<(), nix::Error> {
|
||||
unimplemented!()
|
||||
}
|
||||
fn force_kill(&self, pid: Pid) -> Result<(), nix::Error> {
|
||||
fn force_kill(&self, _pid: Pid) -> Result<(), nix::Error> {
|
||||
unimplemented!()
|
||||
}
|
||||
fn box_clone(&self) -> Box<dyn ServerOsApi> {
|
||||
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!()
|
||||
}
|
||||
fn new_client(
|
||||
&mut self,
|
||||
client_id: ClientId,
|
||||
stream: LocalSocketStream,
|
||||
_client_id: ClientId,
|
||||
_stream: LocalSocketStream,
|
||||
) -> IpcReceiverWithContext<ClientToServerMsg> {
|
||||
unimplemented!()
|
||||
}
|
||||
fn remove_client(&mut self, client_id: ClientId) {
|
||||
fn remove_client(&mut self, _client_id: ClientId) {
|
||||
unimplemented!()
|
||||
}
|
||||
fn load_palette(&self) -> Palette {
|
||||
@ -84,7 +83,8 @@ fn create_new_tab(size: Size) -> Tab {
|
||||
let max_panes = None;
|
||||
let mode_info = ModeInfo::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(
|
||||
index,
|
||||
position,
|
||||
@ -95,8 +95,8 @@ fn create_new_tab(size: Size) -> Tab {
|
||||
max_panes,
|
||||
mode_info,
|
||||
colors,
|
||||
session_state,
|
||||
true, // draw pane frames
|
||||
draw_pane_frames,
|
||||
client_id,
|
||||
);
|
||||
tab.apply_layout(
|
||||
LayoutTemplate::default().try_into().unwrap(),
|
||||
|
@ -244,7 +244,6 @@ pub enum ScreenContext {
|
||||
SetFixedHeight,
|
||||
SetFixedWidth,
|
||||
ClosePane,
|
||||
ApplyLayout,
|
||||
NewTab,
|
||||
SwitchTabNext,
|
||||
SwitchTabPrev,
|
||||
@ -258,6 +257,8 @@ pub enum ScreenContext {
|
||||
MouseHold,
|
||||
Copy,
|
||||
ToggleTab,
|
||||
AddClient,
|
||||
RemoveClient,
|
||||
}
|
||||
|
||||
/// Stack call representations corresponding to the different types of [`PtyInstruction`]s.
|
||||
|
Loading…
Reference in New Issue
Block a user