Add ability to detach a session
This commit is contained in:
parent
2487256664
commit
61aa104576
9 changed files with 85 additions and 27 deletions
|
|
@ -58,7 +58,7 @@ fn list_sessions() {
|
||||||
match fs::read_dir(&*ZELLIJ_SOCK_DIR) {
|
match fs::read_dir(&*ZELLIJ_SOCK_DIR) {
|
||||||
Ok(files) => {
|
Ok(files) => {
|
||||||
let mut is_empty = true;
|
let mut is_empty = true;
|
||||||
let session_name = std::env::var("ZELLIJ_SESSION_NAME").unwrap_or("".into());
|
let session_name = std::env::var("ZELLIJ_SESSION_NAME").unwrap_or_else(|_| "".into());
|
||||||
files.for_each(|file| {
|
files.for_each(|file| {
|
||||||
let file = file.unwrap();
|
let file = file.unwrap();
|
||||||
if file.file_type().unwrap().is_socket() {
|
if file.file_type().unwrap().is_socket() {
|
||||||
|
|
|
||||||
|
|
@ -299,6 +299,7 @@ impl ServerOsApi for FakeInputOutput {
|
||||||
self.send_instructions_to_client.send(msg).unwrap();
|
self.send_instructions_to_client.send(msg).unwrap();
|
||||||
}
|
}
|
||||||
fn add_client_sender(&self) {}
|
fn add_client_sender(&self) {}
|
||||||
|
fn remove_client_sender(&self) {}
|
||||||
fn update_receiver(&mut self, _stream: LocalSocketStream) {}
|
fn update_receiver(&mut self, _stream: LocalSocketStream) {}
|
||||||
fn load_palette(&self) -> Palette {
|
fn load_palette(&self) -> Palette {
|
||||||
default_palette()
|
default_palette()
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,7 @@ pub(crate) enum ServerInstruction {
|
||||||
UnblockInputThread,
|
UnblockInputThread,
|
||||||
ClientExit,
|
ClientExit,
|
||||||
Error(String),
|
Error(String),
|
||||||
|
DetachSession,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ClientToServerMsg> for ServerInstruction {
|
impl From<ClientToServerMsg> for ServerInstruction {
|
||||||
|
|
@ -52,6 +53,7 @@ impl From<ClientToServerMsg> for ServerInstruction {
|
||||||
ClientToServerMsg::NewClient(pos, opts, options) => {
|
ClientToServerMsg::NewClient(pos, opts, options) => {
|
||||||
ServerInstruction::NewClient(pos, opts, options)
|
ServerInstruction::NewClient(pos, opts, options)
|
||||||
}
|
}
|
||||||
|
ClientToServerMsg::DetachSession => ServerInstruction::DetachSession,
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -65,6 +67,7 @@ impl From<&ServerInstruction> for ServerContext {
|
||||||
ServerInstruction::UnblockInputThread => ServerContext::UnblockInputThread,
|
ServerInstruction::UnblockInputThread => ServerContext::UnblockInputThread,
|
||||||
ServerInstruction::ClientExit => ServerContext::ClientExit,
|
ServerInstruction::ClientExit => ServerContext::ClientExit,
|
||||||
ServerInstruction::Error(_) => ServerContext::Error,
|
ServerInstruction::Error(_) => ServerContext::Error,
|
||||||
|
ServerInstruction::DetachSession => ServerContext::DetachSession,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -94,6 +97,13 @@ impl Drop for SessionMetaData {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub(crate) enum SessionState {
|
||||||
|
Attached,
|
||||||
|
Detached,
|
||||||
|
Uninitialized,
|
||||||
|
}
|
||||||
|
|
||||||
pub fn start_server(os_input: Box<dyn ServerOsApi>, socket_path: PathBuf) {
|
pub fn start_server(os_input: Box<dyn ServerOsApi>, socket_path: PathBuf) {
|
||||||
#[cfg(not(any(feature = "test", test)))]
|
#[cfg(not(any(feature = "test", test)))]
|
||||||
daemonize::Daemonize::new()
|
daemonize::Daemonize::new()
|
||||||
|
|
@ -107,7 +117,8 @@ pub fn start_server(os_input: Box<dyn ServerOsApi>, socket_path: PathBuf) {
|
||||||
let (to_server, server_receiver): SyncChannelWithContext<ServerInstruction> =
|
let (to_server, server_receiver): SyncChannelWithContext<ServerInstruction> =
|
||||||
mpsc::sync_channel(50);
|
mpsc::sync_channel(50);
|
||||||
let to_server = SenderWithContext::new(SenderType::SyncSender(to_server));
|
let to_server = SenderWithContext::new(SenderType::SyncSender(to_server));
|
||||||
let sessions: Arc<RwLock<Option<SessionMetaData>>> = Arc::new(RwLock::new(None));
|
let session_data: Arc<RwLock<Option<SessionMetaData>>> = Arc::new(RwLock::new(None));
|
||||||
|
let session_state = Arc::new(RwLock::new(SessionState::Uninitialized));
|
||||||
|
|
||||||
#[cfg(not(any(feature = "test", test)))]
|
#[cfg(not(any(feature = "test", test)))]
|
||||||
std::panic::set_hook({
|
std::panic::set_hook({
|
||||||
|
|
@ -122,11 +133,11 @@ pub fn start_server(os_input: Box<dyn ServerOsApi>, socket_path: PathBuf) {
|
||||||
thread::Builder::new()
|
thread::Builder::new()
|
||||||
.name("server_router".to_string())
|
.name("server_router".to_string())
|
||||||
.spawn({
|
.spawn({
|
||||||
let sessions = sessions.clone();
|
let session_data = session_data.clone();
|
||||||
let os_input = os_input.clone();
|
let os_input = os_input.clone();
|
||||||
let to_server = to_server.clone();
|
let to_server = to_server.clone();
|
||||||
|
|
||||||
move || route_thread_main(sessions, os_input, to_server)
|
move || route_thread_main(session_data, os_input, to_server)
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
#[cfg(not(any(feature = "test", test)))]
|
#[cfg(not(any(feature = "test", test)))]
|
||||||
|
|
@ -138,7 +149,7 @@ pub fn start_server(os_input: Box<dyn ServerOsApi>, socket_path: PathBuf) {
|
||||||
};
|
};
|
||||||
|
|
||||||
let os_input = os_input.clone();
|
let os_input = os_input.clone();
|
||||||
let sessions = sessions.clone();
|
let session_data = session_data.clone();
|
||||||
let to_server = to_server.clone();
|
let to_server = to_server.clone();
|
||||||
let socket_path = socket_path.clone();
|
let socket_path = socket_path.clone();
|
||||||
move || {
|
move || {
|
||||||
|
|
@ -150,16 +161,16 @@ pub fn start_server(os_input: Box<dyn ServerOsApi>, socket_path: PathBuf) {
|
||||||
Ok(stream) => {
|
Ok(stream) => {
|
||||||
let mut os_input = os_input.clone();
|
let mut os_input = os_input.clone();
|
||||||
os_input.update_receiver(stream);
|
os_input.update_receiver(stream);
|
||||||
let sessions = sessions.clone();
|
let session_data = session_data.clone();
|
||||||
let to_server = to_server.clone();
|
let to_server = to_server.clone();
|
||||||
thread::Builder::new()
|
thread::Builder::new()
|
||||||
.name("server_router".to_string())
|
.name("server_router".to_string())
|
||||||
.spawn({
|
.spawn({
|
||||||
let sessions = sessions.clone();
|
let session_data = session_data.clone();
|
||||||
let os_input = os_input.clone();
|
let os_input = os_input.clone();
|
||||||
let to_server = to_server.clone();
|
let to_server = to_server.clone();
|
||||||
|
|
||||||
move || route_thread_main(sessions, os_input, to_server)
|
move || route_thread_main(session_data, os_input, to_server)
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
@ -176,15 +187,17 @@ pub fn start_server(os_input: Box<dyn ServerOsApi>, socket_path: PathBuf) {
|
||||||
err_ctx.add_call(ContextType::IPCServer((&instruction).into()));
|
err_ctx.add_call(ContextType::IPCServer((&instruction).into()));
|
||||||
match instruction {
|
match instruction {
|
||||||
ServerInstruction::NewClient(client_attributes, opts, config_options) => {
|
ServerInstruction::NewClient(client_attributes, opts, config_options) => {
|
||||||
let session_data = init_session(
|
let session = init_session(
|
||||||
os_input.clone(),
|
os_input.clone(),
|
||||||
opts,
|
opts,
|
||||||
config_options,
|
config_options,
|
||||||
to_server.clone(),
|
to_server.clone(),
|
||||||
client_attributes,
|
client_attributes,
|
||||||
|
session_state.clone(),
|
||||||
);
|
);
|
||||||
*sessions.write().unwrap() = Some(session_data);
|
*session_data.write().unwrap() = Some(session);
|
||||||
sessions
|
*session_state.write().unwrap() = SessionState::Attached;
|
||||||
|
session_data
|
||||||
.read()
|
.read()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
|
@ -194,18 +207,29 @@ pub fn start_server(os_input: Box<dyn ServerOsApi>, socket_path: PathBuf) {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
ServerInstruction::UnblockInputThread => {
|
ServerInstruction::UnblockInputThread => {
|
||||||
os_input.send_to_client(ServerToClientMsg::UnblockInputThread);
|
if *session_state.read().unwrap() == SessionState::Attached {
|
||||||
|
os_input.send_to_client(ServerToClientMsg::UnblockInputThread);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ServerInstruction::ClientExit => {
|
ServerInstruction::ClientExit => {
|
||||||
*sessions.write().unwrap() = None;
|
*session_data.write().unwrap() = None;
|
||||||
os_input.send_to_client(ServerToClientMsg::Exit);
|
os_input.send_to_client(ServerToClientMsg::Exit);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
ServerInstruction::DetachSession => {
|
||||||
|
*session_state.write().unwrap() = SessionState::Detached;
|
||||||
|
os_input.send_to_client(ServerToClientMsg::Exit);
|
||||||
|
os_input.remove_client_sender();
|
||||||
|
}
|
||||||
ServerInstruction::Render(output) => {
|
ServerInstruction::Render(output) => {
|
||||||
os_input.send_to_client(ServerToClientMsg::Render(output))
|
if *session_state.read().unwrap() == SessionState::Attached {
|
||||||
|
os_input.send_to_client(ServerToClientMsg::Render(output));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ServerInstruction::Error(backtrace) => {
|
ServerInstruction::Error(backtrace) => {
|
||||||
os_input.send_to_client(ServerToClientMsg::ServerError(backtrace));
|
if *session_state.read().unwrap() == SessionState::Attached {
|
||||||
|
os_input.send_to_client(ServerToClientMsg::ServerError(backtrace));
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -220,6 +244,7 @@ fn init_session(
|
||||||
config_options: Box<Options>,
|
config_options: Box<Options>,
|
||||||
to_server: SenderWithContext<ServerInstruction>,
|
to_server: SenderWithContext<ServerInstruction>,
|
||||||
client_attributes: ClientAttributes,
|
client_attributes: ClientAttributes,
|
||||||
|
session_state: Arc<RwLock<SessionState>>,
|
||||||
) -> SessionMetaData {
|
) -> SessionMetaData {
|
||||||
let (to_screen, screen_receiver): ChannelWithContext<ScreenInstruction> = mpsc::channel();
|
let (to_screen, screen_receiver): ChannelWithContext<ScreenInstruction> = mpsc::channel();
|
||||||
let to_screen = SenderWithContext::new(SenderType::Sender(to_screen));
|
let to_screen = SenderWithContext::new(SenderType::Sender(to_screen));
|
||||||
|
|
@ -285,7 +310,13 @@ fn init_session(
|
||||||
let max_panes = opts.max_panes;
|
let max_panes = opts.max_panes;
|
||||||
|
|
||||||
move || {
|
move || {
|
||||||
screen_thread_main(screen_bus, max_panes, client_attributes, config_options);
|
screen_thread_main(
|
||||||
|
screen_bus,
|
||||||
|
max_panes,
|
||||||
|
client_attributes,
|
||||||
|
config_options,
|
||||||
|
session_state,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
|
||||||
|
|
@ -156,6 +156,8 @@ pub trait ServerOsApi: Send + Sync {
|
||||||
fn send_to_client(&self, msg: ServerToClientMsg);
|
fn send_to_client(&self, msg: ServerToClientMsg);
|
||||||
/// Adds a sender to client
|
/// Adds a sender to client
|
||||||
fn add_client_sender(&self);
|
fn add_client_sender(&self);
|
||||||
|
/// Removes the sender to client
|
||||||
|
fn remove_client_sender(&self);
|
||||||
/// Update the receiver socket for the client
|
/// Update the receiver socket for the client
|
||||||
fn update_receiver(&mut self, stream: LocalSocketStream);
|
fn update_receiver(&mut self, stream: LocalSocketStream);
|
||||||
fn load_palette(&self) -> Palette;
|
fn load_palette(&self) -> Palette;
|
||||||
|
|
@ -219,6 +221,10 @@ impl ServerOsApi for ServerOsInputOutput {
|
||||||
.get_sender();
|
.get_sender();
|
||||||
*self.send_instructions_to_client.lock().unwrap() = Some(sender);
|
*self.send_instructions_to_client.lock().unwrap() = Some(sender);
|
||||||
}
|
}
|
||||||
|
fn remove_client_sender(&self) {
|
||||||
|
assert!(self.send_instructions_to_client.lock().unwrap().is_some());
|
||||||
|
*self.send_instructions_to_client.lock().unwrap() = None;
|
||||||
|
}
|
||||||
fn update_receiver(&mut self, stream: LocalSocketStream) {
|
fn update_receiver(&mut self, stream: LocalSocketStream) {
|
||||||
self.receive_instructions_from_client =
|
self.receive_instructions_from_client =
|
||||||
Some(Arc::new(Mutex::new(IpcReceiverWithContext::new(stream))));
|
Some(Arc::new(Mutex::new(IpcReceiverWithContext::new(stream))));
|
||||||
|
|
|
||||||
|
|
@ -188,16 +188,16 @@ fn route_action(action: Action, session: &SessionMetaData, os_input: &dyn Server
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn route_thread_main(
|
pub(crate) fn route_thread_main(
|
||||||
sessions: Arc<RwLock<Option<SessionMetaData>>>,
|
session_data: Arc<RwLock<Option<SessionMetaData>>>,
|
||||||
os_input: Box<dyn ServerOsApi>,
|
os_input: Box<dyn ServerOsApi>,
|
||||||
to_server: SenderWithContext<ServerInstruction>,
|
to_server: SenderWithContext<ServerInstruction>,
|
||||||
) {
|
) {
|
||||||
loop {
|
loop {
|
||||||
let (instruction, err_ctx) = os_input.recv_from_client();
|
let (instruction, err_ctx) = os_input.recv_from_client();
|
||||||
err_ctx.update_thread_ctx();
|
err_ctx.update_thread_ctx();
|
||||||
let rlocked_sessions = sessions.read().unwrap();
|
let rlocked_sessions = session_data.read().unwrap();
|
||||||
match instruction {
|
match instruction {
|
||||||
ClientToServerMsg::ClientExit => {
|
ClientToServerMsg::ClientExit | ClientToServerMsg::DetachSession => {
|
||||||
to_server.send(instruction.into()).unwrap();
|
to_server.send(instruction.into()).unwrap();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
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::zellij_tile;
|
use zellij_utils::zellij_tile;
|
||||||
|
|
||||||
|
|
@ -13,7 +14,7 @@ use crate::{
|
||||||
thread_bus::Bus,
|
thread_bus::Bus,
|
||||||
ui::layout::Layout,
|
ui::layout::Layout,
|
||||||
wasm_vm::PluginInstruction,
|
wasm_vm::PluginInstruction,
|
||||||
ServerInstruction,
|
ServerInstruction, SessionState,
|
||||||
};
|
};
|
||||||
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::{
|
||||||
|
|
@ -137,6 +138,7 @@ pub(crate) struct Screen {
|
||||||
mode_info: ModeInfo,
|
mode_info: ModeInfo,
|
||||||
input_mode: InputMode,
|
input_mode: InputMode,
|
||||||
colors: Palette,
|
colors: Palette,
|
||||||
|
session_state: Arc<RwLock<SessionState>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Screen {
|
impl Screen {
|
||||||
|
|
@ -147,6 +149,7 @@ impl Screen {
|
||||||
max_panes: Option<usize>,
|
max_panes: Option<usize>,
|
||||||
mode_info: ModeInfo,
|
mode_info: ModeInfo,
|
||||||
input_mode: InputMode,
|
input_mode: InputMode,
|
||||||
|
session_state: Arc<RwLock<SessionState>>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Screen {
|
Screen {
|
||||||
bus,
|
bus,
|
||||||
|
|
@ -157,6 +160,7 @@ impl Screen {
|
||||||
tabs: BTreeMap::new(),
|
tabs: BTreeMap::new(),
|
||||||
mode_info,
|
mode_info,
|
||||||
input_mode,
|
input_mode,
|
||||||
|
session_state,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -177,6 +181,7 @@ impl Screen {
|
||||||
self.mode_info.clone(),
|
self.mode_info.clone(),
|
||||||
self.input_mode,
|
self.input_mode,
|
||||||
self.colors,
|
self.colors,
|
||||||
|
self.session_state.clone(),
|
||||||
);
|
);
|
||||||
self.active_tab_index = Some(tab_index);
|
self.active_tab_index = Some(tab_index);
|
||||||
self.tabs.insert(tab_index, tab);
|
self.tabs.insert(tab_index, tab);
|
||||||
|
|
@ -261,10 +266,12 @@ impl Screen {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
if self.tabs.is_empty() {
|
if self.tabs.is_empty() {
|
||||||
self.active_tab_index = None;
|
self.active_tab_index = None;
|
||||||
self.bus
|
if *self.session_state.read().unwrap() == SessionState::Attached {
|
||||||
.senders
|
self.bus
|
||||||
.send_to_server(ServerInstruction::Render(None))
|
.senders
|
||||||
.unwrap();
|
.send_to_server(ServerInstruction::Render(None))
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
for t in self.tabs.values_mut() {
|
for t in self.tabs.values_mut() {
|
||||||
if t.position > active_tab.position {
|
if t.position > active_tab.position {
|
||||||
|
|
@ -286,6 +293,9 @@ impl Screen {
|
||||||
|
|
||||||
/// Renders this [`Screen`], which amounts to rendering its active [`Tab`].
|
/// Renders this [`Screen`], which amounts to rendering its active [`Tab`].
|
||||||
pub fn render(&mut self) {
|
pub fn render(&mut self) {
|
||||||
|
if *self.session_state.read().unwrap() != SessionState::Attached {
|
||||||
|
return;
|
||||||
|
}
|
||||||
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();
|
active_tab.render();
|
||||||
|
|
@ -333,6 +343,7 @@ impl Screen {
|
||||||
self.mode_info.clone(),
|
self.mode_info.clone(),
|
||||||
self.input_mode,
|
self.input_mode,
|
||||||
self.colors,
|
self.colors,
|
||||||
|
self.session_state.clone(),
|
||||||
);
|
);
|
||||||
tab.apply_layout(layout, new_pids);
|
tab.apply_layout(layout, new_pids);
|
||||||
self.active_tab_index = Some(tab_index);
|
self.active_tab_index = Some(tab_index);
|
||||||
|
|
@ -390,6 +401,7 @@ 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;
|
||||||
|
|
||||||
|
|
@ -405,6 +417,7 @@ pub(crate) fn screen_thread_main(
|
||||||
..ModeInfo::default()
|
..ModeInfo::default()
|
||||||
},
|
},
|
||||||
InputMode::Normal,
|
InputMode::Normal,
|
||||||
|
session_state,
|
||||||
);
|
);
|
||||||
loop {
|
loop {
|
||||||
let (event, mut err_ctx) = screen
|
let (event, mut err_ctx) = screen
|
||||||
|
|
|
||||||
|
|
@ -10,11 +10,11 @@ use crate::{
|
||||||
thread_bus::ThreadSenders,
|
thread_bus::ThreadSenders,
|
||||||
ui::{boundaries::Boundaries, layout::Layout, pane_resizer::PaneResizer},
|
ui::{boundaries::Boundaries, layout::Layout, pane_resizer::PaneResizer},
|
||||||
wasm_vm::PluginInstruction,
|
wasm_vm::PluginInstruction,
|
||||||
ServerInstruction,
|
ServerInstruction, SessionState,
|
||||||
};
|
};
|
||||||
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;
|
use std::sync::{mpsc::channel, Arc, RwLock};
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
use std::{
|
use std::{
|
||||||
cmp::Reverse,
|
cmp::Reverse,
|
||||||
|
|
@ -74,6 +74,7 @@ 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 input_mode: InputMode,
|
pub input_mode: InputMode,
|
||||||
pub colors: Palette,
|
pub colors: Palette,
|
||||||
|
|
@ -242,6 +243,7 @@ impl Tab {
|
||||||
mode_info: ModeInfo,
|
mode_info: ModeInfo,
|
||||||
input_mode: InputMode,
|
input_mode: InputMode,
|
||||||
colors: Palette,
|
colors: Palette,
|
||||||
|
session_state: Arc<RwLock<SessionState>>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let panes = if let Some(PaneId::Terminal(pid)) = pane_id {
|
let panes = if let Some(PaneId::Terminal(pid)) = pane_id {
|
||||||
let new_terminal = TerminalPane::new(pid, *full_screen_ws, colors);
|
let new_terminal = TerminalPane::new(pid, *full_screen_ws, colors);
|
||||||
|
|
@ -273,6 +275,7 @@ impl Tab {
|
||||||
mode_info,
|
mode_info,
|
||||||
input_mode,
|
input_mode,
|
||||||
colors,
|
colors,
|
||||||
|
session_state,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -722,7 +725,9 @@ impl Tab {
|
||||||
self.panes.iter().any(|(_, p)| p.contains_widechar())
|
self.panes.iter().any(|(_, p)| p.contains_widechar())
|
||||||
}
|
}
|
||||||
pub fn render(&mut self) {
|
pub fn render(&mut self) {
|
||||||
if self.active_terminal.is_none() {
|
if self.active_terminal.is_none()
|
||||||
|
|| *self.session_state.read().unwrap() != SessionState::Attached
|
||||||
|
{
|
||||||
// we might not have an active terminal if we closed the last pane
|
// 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
|
// in that case, we should not render as the app is exiting
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -260,4 +260,5 @@ pub enum ServerContext {
|
||||||
UnblockInputThread,
|
UnblockInputThread,
|
||||||
ClientExit,
|
ClientExit,
|
||||||
Error,
|
Error,
|
||||||
|
DetachSession,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,7 @@ pub enum ClientToServerMsg {
|
||||||
TerminalResize(PositionAndSize),
|
TerminalResize(PositionAndSize),
|
||||||
NewClient(ClientAttributes, Box<CliArgs>, Box<Options>),
|
NewClient(ClientAttributes, Box<CliArgs>, Box<Options>),
|
||||||
Action(Action),
|
Action(Action),
|
||||||
|
DetachSession,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Types of messages sent from the server to the client
|
// Types of messages sent from the server to the client
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue