feat(terminals): send focus in/out events to terminal panes (#1908)
* feat(terminals): send focus in/out events to terminal panes * style(fmt): rustfmt * style(fmt): rustfmt
This commit is contained in:
parent
c34853adac
commit
8d1a497d10
14 changed files with 480 additions and 55 deletions
100
zellij-server/src/panes/active_panes.rs
Normal file
100
zellij-server/src/panes/active_panes.rs
Normal file
|
|
@ -0,0 +1,100 @@
|
||||||
|
use crate::tab::Pane;
|
||||||
|
|
||||||
|
use crate::{os_input_output::ServerOsApi, panes::PaneId, ClientId};
|
||||||
|
use std::collections::{BTreeMap, HashMap};
|
||||||
|
|
||||||
|
pub struct ActivePanes {
|
||||||
|
active_panes: HashMap<ClientId, PaneId>,
|
||||||
|
os_api: Box<dyn ServerOsApi>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ActivePanes {
|
||||||
|
pub fn new(os_api: &Box<dyn ServerOsApi>) -> Self {
|
||||||
|
let os_api = os_api.clone();
|
||||||
|
ActivePanes {
|
||||||
|
active_panes: HashMap::new(),
|
||||||
|
os_api,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn get(&self, client_id: &ClientId) -> Option<&PaneId> {
|
||||||
|
self.active_panes.get(client_id)
|
||||||
|
}
|
||||||
|
pub fn insert(
|
||||||
|
&mut self,
|
||||||
|
client_id: ClientId,
|
||||||
|
pane_id: PaneId,
|
||||||
|
panes: &mut BTreeMap<PaneId, Box<dyn Pane>>,
|
||||||
|
) {
|
||||||
|
self.unfocus_pane_for_client(client_id, panes);
|
||||||
|
self.active_panes.insert(client_id, pane_id);
|
||||||
|
self.focus_pane(pane_id, panes);
|
||||||
|
}
|
||||||
|
pub fn clear(&mut self, panes: &mut BTreeMap<PaneId, Box<dyn Pane>>) {
|
||||||
|
for pane_id in self.active_panes.values() {
|
||||||
|
self.unfocus_pane(*pane_id, panes);
|
||||||
|
}
|
||||||
|
self.active_panes.clear();
|
||||||
|
}
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.active_panes.is_empty()
|
||||||
|
}
|
||||||
|
pub fn iter(&self) -> impl Iterator<Item = (&ClientId, &PaneId)> {
|
||||||
|
self.active_panes.iter()
|
||||||
|
}
|
||||||
|
pub fn values(&self) -> impl Iterator<Item = &PaneId> {
|
||||||
|
self.active_panes.values()
|
||||||
|
}
|
||||||
|
pub fn remove(
|
||||||
|
&mut self,
|
||||||
|
client_id: &ClientId,
|
||||||
|
panes: &mut BTreeMap<PaneId, Box<dyn Pane>>,
|
||||||
|
) -> Option<PaneId> {
|
||||||
|
if let Some(pane_id_to_unfocus) = self.active_panes.get(&client_id) {
|
||||||
|
self.unfocus_pane(*pane_id_to_unfocus, panes);
|
||||||
|
}
|
||||||
|
self.active_panes.remove(client_id)
|
||||||
|
}
|
||||||
|
pub fn unfocus_all_panes(&self, panes: &mut BTreeMap<PaneId, Box<dyn Pane>>) {
|
||||||
|
for (_client_id, pane_id) in &self.active_panes {
|
||||||
|
self.unfocus_pane(*pane_id, panes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn focus_all_panes(&self, panes: &mut BTreeMap<PaneId, Box<dyn Pane>>) {
|
||||||
|
for (_client_id, pane_id) in &self.active_panes {
|
||||||
|
self.focus_pane(*pane_id, panes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn clone_active_panes(&self) -> HashMap<ClientId, PaneId> {
|
||||||
|
self.active_panes.clone()
|
||||||
|
}
|
||||||
|
pub fn contains_key(&self, client_id: &ClientId) -> bool {
|
||||||
|
self.active_panes.contains_key(client_id)
|
||||||
|
}
|
||||||
|
fn unfocus_pane_for_client(
|
||||||
|
&self,
|
||||||
|
client_id: ClientId,
|
||||||
|
panes: &mut BTreeMap<PaneId, Box<dyn Pane>>,
|
||||||
|
) {
|
||||||
|
if let Some(pane_id_to_unfocus) = self.active_panes.get(&client_id) {
|
||||||
|
self.unfocus_pane(*pane_id_to_unfocus, panes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn unfocus_pane(&self, pane_id: PaneId, panes: &mut BTreeMap<PaneId, Box<dyn Pane>>) {
|
||||||
|
if let PaneId::Terminal(terminal_id) = pane_id {
|
||||||
|
if let Some(focus_event) = panes.get(&pane_id).and_then(|p| p.unfocus_event()) {
|
||||||
|
let _ = self
|
||||||
|
.os_api
|
||||||
|
.write_to_tty_stdin(terminal_id, focus_event.as_bytes());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn focus_pane(&self, pane_id: PaneId, panes: &mut BTreeMap<PaneId, Box<dyn Pane>>) {
|
||||||
|
if let PaneId::Terminal(terminal_id) = pane_id {
|
||||||
|
if let Some(focus_event) = panes.get(&pane_id).and_then(|p| p.focus_event()) {
|
||||||
|
let _ = self
|
||||||
|
.os_api
|
||||||
|
.write_to_tty_stdin(terminal_id, focus_event.as_bytes());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -7,7 +7,7 @@ use floating_pane_grid::FloatingPaneGrid;
|
||||||
use crate::{
|
use crate::{
|
||||||
os_input_output::ServerOsApi,
|
os_input_output::ServerOsApi,
|
||||||
output::{FloatingPanesStack, Output},
|
output::{FloatingPanesStack, Output},
|
||||||
panes::PaneId,
|
panes::{ActivePanes, PaneId},
|
||||||
ui::pane_contents_and_ui::PaneContentsAndUi,
|
ui::pane_contents_and_ui::PaneContentsAndUi,
|
||||||
ClientId,
|
ClientId,
|
||||||
};
|
};
|
||||||
|
|
@ -50,7 +50,7 @@ pub struct FloatingPanes {
|
||||||
session_is_mirrored: bool,
|
session_is_mirrored: bool,
|
||||||
desired_pane_positions: HashMap<PaneId, PaneGeom>, // this represents the positions of panes the user moved with intention, rather than by resizing the terminal window
|
desired_pane_positions: HashMap<PaneId, PaneGeom>, // this represents the positions of panes the user moved with intention, rather than by resizing the terminal window
|
||||||
z_indices: Vec<PaneId>,
|
z_indices: Vec<PaneId>,
|
||||||
active_panes: HashMap<ClientId, PaneId>,
|
active_panes: ActivePanes,
|
||||||
show_panes: bool,
|
show_panes: bool,
|
||||||
pane_being_moved_with_mouse: Option<(PaneId, Position)>,
|
pane_being_moved_with_mouse: Option<(PaneId, Position)>,
|
||||||
}
|
}
|
||||||
|
|
@ -67,6 +67,7 @@ impl FloatingPanes {
|
||||||
session_is_mirrored: bool,
|
session_is_mirrored: bool,
|
||||||
default_mode_info: ModeInfo,
|
default_mode_info: ModeInfo,
|
||||||
style: Style,
|
style: Style,
|
||||||
|
os_input: Box<dyn ServerOsApi>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
FloatingPanes {
|
FloatingPanes {
|
||||||
panes: BTreeMap::new(),
|
panes: BTreeMap::new(),
|
||||||
|
|
@ -81,7 +82,7 @@ impl FloatingPanes {
|
||||||
desired_pane_positions: HashMap::new(),
|
desired_pane_positions: HashMap::new(),
|
||||||
z_indices: vec![],
|
z_indices: vec![],
|
||||||
show_panes: false,
|
show_panes: false,
|
||||||
active_panes: HashMap::new(),
|
active_panes: ActivePanes::new(&os_input),
|
||||||
pane_being_moved_with_mouse: None,
|
pane_being_moved_with_mouse: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -195,6 +196,11 @@ impl FloatingPanes {
|
||||||
}
|
}
|
||||||
pub fn toggle_show_panes(&mut self, should_show_floating_panes: bool) {
|
pub fn toggle_show_panes(&mut self, should_show_floating_panes: bool) {
|
||||||
self.show_panes = should_show_floating_panes;
|
self.show_panes = should_show_floating_panes;
|
||||||
|
if should_show_floating_panes {
|
||||||
|
self.active_panes.focus_all_panes(&mut self.panes);
|
||||||
|
} else {
|
||||||
|
self.active_panes.unfocus_all_panes(&mut self.panes);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
pub fn active_panes_contain(&self, client_id: &ClientId) -> bool {
|
pub fn active_panes_contain(&self, client_id: &ClientId) -> bool {
|
||||||
self.active_panes.contains_key(client_id)
|
self.active_panes.contains_key(client_id)
|
||||||
|
|
@ -260,7 +266,7 @@ impl FloatingPanes {
|
||||||
});
|
});
|
||||||
|
|
||||||
for (z_index, (kind, pane)) in floating_panes.iter_mut().enumerate() {
|
for (z_index, (kind, pane)) in floating_panes.iter_mut().enumerate() {
|
||||||
let mut active_panes = self.active_panes.clone();
|
let mut active_panes = self.active_panes.clone_active_panes();
|
||||||
let multiple_users_exist_in_session =
|
let multiple_users_exist_in_session =
|
||||||
{ self.connected_clients_in_app.borrow().len() > 1 };
|
{ self.connected_clients_in_app.borrow().len() > 1 };
|
||||||
active_panes.retain(|c_id, _| self.connected_clients.borrow().contains(c_id));
|
active_panes.retain(|c_id, _| self.connected_clients.borrow().contains(c_id));
|
||||||
|
|
@ -532,7 +538,7 @@ impl FloatingPanes {
|
||||||
},
|
},
|
||||||
None => {
|
None => {
|
||||||
// TODO: can this happen?
|
// TODO: can this happen?
|
||||||
self.active_panes.clear();
|
self.active_panes.clear(&mut self.panes);
|
||||||
self.z_indices.clear();
|
self.z_indices.clear();
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
@ -603,7 +609,7 @@ impl FloatingPanes {
|
||||||
},
|
},
|
||||||
None => {
|
None => {
|
||||||
// TODO: can this happen?
|
// TODO: can this happen?
|
||||||
self.active_panes.clear();
|
self.active_panes.clear(&mut self.panes);
|
||||||
self.z_indices.clear();
|
self.z_indices.clear();
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
@ -673,7 +679,7 @@ impl FloatingPanes {
|
||||||
},
|
},
|
||||||
None => {
|
None => {
|
||||||
// TODO: can this happen?
|
// TODO: can this happen?
|
||||||
self.active_panes.clear();
|
self.active_panes.clear(&mut self.panes);
|
||||||
self.z_indices.clear();
|
self.z_indices.clear();
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
@ -743,7 +749,7 @@ impl FloatingPanes {
|
||||||
},
|
},
|
||||||
None => {
|
None => {
|
||||||
// TODO: can this happen?
|
// TODO: can this happen?
|
||||||
self.active_panes.clear();
|
self.active_panes.clear(&mut self.panes);
|
||||||
self.z_indices.clear();
|
self.z_indices.clear();
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
@ -816,7 +822,8 @@ impl FloatingPanes {
|
||||||
if active_pane_id == pane_id {
|
if active_pane_id == pane_id {
|
||||||
match next_active_pane {
|
match next_active_pane {
|
||||||
Some(next_active_pane) => {
|
Some(next_active_pane) => {
|
||||||
self.active_panes.insert(client_id, next_active_pane);
|
self.active_panes
|
||||||
|
.insert(client_id, next_active_pane, &mut self.panes);
|
||||||
self.focus_pane(next_active_pane, client_id);
|
self.focus_pane(next_active_pane, client_id);
|
||||||
},
|
},
|
||||||
None => {
|
None => {
|
||||||
|
|
@ -830,7 +837,8 @@ impl FloatingPanes {
|
||||||
let connected_clients: Vec<ClientId> =
|
let connected_clients: Vec<ClientId> =
|
||||||
self.connected_clients.borrow().iter().copied().collect();
|
self.connected_clients.borrow().iter().copied().collect();
|
||||||
for client_id in connected_clients {
|
for client_id in connected_clients {
|
||||||
self.active_panes.insert(client_id, pane_id);
|
self.active_panes
|
||||||
|
.insert(client_id, pane_id, &mut self.panes);
|
||||||
}
|
}
|
||||||
self.z_indices.retain(|p_id| *p_id != pane_id);
|
self.z_indices.retain(|p_id| *p_id != pane_id);
|
||||||
self.z_indices.push(pane_id);
|
self.z_indices.push(pane_id);
|
||||||
|
|
@ -838,12 +846,13 @@ impl FloatingPanes {
|
||||||
self.set_force_render();
|
self.set_force_render();
|
||||||
}
|
}
|
||||||
pub fn focus_pane(&mut self, pane_id: PaneId, client_id: ClientId) {
|
pub fn focus_pane(&mut self, pane_id: PaneId, client_id: ClientId) {
|
||||||
self.active_panes.insert(client_id, pane_id);
|
self.active_panes
|
||||||
|
.insert(client_id, pane_id, &mut self.panes);
|
||||||
self.focus_pane_for_all_clients(pane_id);
|
self.focus_pane_for_all_clients(pane_id);
|
||||||
}
|
}
|
||||||
pub fn defocus_pane(&mut self, pane_id: PaneId, client_id: ClientId) {
|
pub fn defocus_pane(&mut self, pane_id: PaneId, client_id: ClientId) {
|
||||||
self.z_indices.retain(|p_id| *p_id != pane_id);
|
self.z_indices.retain(|p_id| *p_id != pane_id);
|
||||||
self.active_panes.remove(&client_id);
|
self.active_panes.remove(&client_id, &mut self.panes);
|
||||||
self.set_force_render();
|
self.set_force_render();
|
||||||
}
|
}
|
||||||
pub fn get_pane(&self, pane_id: PaneId) -> Option<&Box<dyn Pane>> {
|
pub fn get_pane(&self, pane_id: PaneId) -> Option<&Box<dyn Pane>> {
|
||||||
|
|
@ -960,8 +969,9 @@ impl FloatingPanes {
|
||||||
.map(|(cid, _pid)| *cid)
|
.map(|(cid, _pid)| *cid)
|
||||||
.collect();
|
.collect();
|
||||||
for client_id in clients_in_pane {
|
for client_id in clients_in_pane {
|
||||||
self.active_panes.remove(&client_id);
|
self.active_panes.remove(&client_id, &mut self.panes);
|
||||||
self.active_panes.insert(client_id, to_pane_id);
|
self.active_panes
|
||||||
|
.insert(client_id, to_pane_id, &mut self.panes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -365,6 +365,7 @@ pub struct Grid {
|
||||||
scrollback_buffer_lines: usize,
|
scrollback_buffer_lines: usize,
|
||||||
pub mouse_mode: MouseMode,
|
pub mouse_mode: MouseMode,
|
||||||
pub mouse_tracking: MouseTracking,
|
pub mouse_tracking: MouseTracking,
|
||||||
|
pub focus_event_tracking: bool,
|
||||||
pub search_results: SearchResult,
|
pub search_results: SearchResult,
|
||||||
pub pending_clipboard_update: Option<String>,
|
pub pending_clipboard_update: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
@ -497,6 +498,7 @@ impl Grid {
|
||||||
scrollback_buffer_lines: 0,
|
scrollback_buffer_lines: 0,
|
||||||
mouse_mode: MouseMode::default(),
|
mouse_mode: MouseMode::default(),
|
||||||
mouse_tracking: MouseTracking::default(),
|
mouse_tracking: MouseTracking::default(),
|
||||||
|
focus_event_tracking: false,
|
||||||
character_cell_size,
|
character_cell_size,
|
||||||
search_results: Default::default(),
|
search_results: Default::default(),
|
||||||
sixel_grid,
|
sixel_grid,
|
||||||
|
|
@ -1533,6 +1535,7 @@ impl Grid {
|
||||||
self.sixel_scrolling = false;
|
self.sixel_scrolling = false;
|
||||||
self.mouse_mode = MouseMode::NoEncoding;
|
self.mouse_mode = MouseMode::NoEncoding;
|
||||||
self.mouse_tracking = MouseTracking::Off;
|
self.mouse_tracking = MouseTracking::Off;
|
||||||
|
self.focus_event_tracking = false;
|
||||||
self.cursor_is_hidden = false;
|
self.cursor_is_hidden = false;
|
||||||
if let Some(images_to_reap) = self.sixel_grid.clear() {
|
if let Some(images_to_reap) = self.sixel_grid.clear() {
|
||||||
self.sixel_grid.reap_images(images_to_reap);
|
self.sixel_grid.reap_images(images_to_reap);
|
||||||
|
|
@ -1942,6 +1945,20 @@ impl Grid {
|
||||||
pub fn is_alternate_mode_active(&self) -> bool {
|
pub fn is_alternate_mode_active(&self) -> bool {
|
||||||
self.alternate_screen_state.is_some()
|
self.alternate_screen_state.is_some()
|
||||||
}
|
}
|
||||||
|
pub fn focus_event(&self) -> Option<String> {
|
||||||
|
if self.focus_event_tracking {
|
||||||
|
Some("\u{1b}[I".into())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn unfocus_event(&self) -> Option<String> {
|
||||||
|
if self.focus_event_tracking {
|
||||||
|
Some("\u{1b}[O".into())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Perform for Grid {
|
impl Perform for Grid {
|
||||||
|
|
@ -2356,6 +2373,9 @@ impl Perform for Grid {
|
||||||
1003 => {
|
1003 => {
|
||||||
// TBD: any-even mouse tracking
|
// TBD: any-even mouse tracking
|
||||||
},
|
},
|
||||||
|
1004 => {
|
||||||
|
self.focus_event_tracking = false;
|
||||||
|
},
|
||||||
1005 => {
|
1005 => {
|
||||||
self.mouse_mode = MouseMode::NoEncoding;
|
self.mouse_mode = MouseMode::NoEncoding;
|
||||||
},
|
},
|
||||||
|
|
@ -2450,6 +2470,9 @@ impl Perform for Grid {
|
||||||
1003 => {
|
1003 => {
|
||||||
// TBD: any-even mouse tracking
|
// TBD: any-even mouse tracking
|
||||||
},
|
},
|
||||||
|
1004 => {
|
||||||
|
self.focus_event_tracking = true;
|
||||||
|
},
|
||||||
1005 => {
|
1005 => {
|
||||||
self.mouse_mode = MouseMode::Utf8;
|
self.mouse_mode = MouseMode::Utf8;
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -5,12 +5,14 @@ pub mod selection;
|
||||||
pub mod sixel;
|
pub mod sixel;
|
||||||
pub mod terminal_character;
|
pub mod terminal_character;
|
||||||
|
|
||||||
|
mod active_panes;
|
||||||
mod floating_panes;
|
mod floating_panes;
|
||||||
mod plugin_pane;
|
mod plugin_pane;
|
||||||
mod search;
|
mod search;
|
||||||
mod terminal_pane;
|
mod terminal_pane;
|
||||||
mod tiled_panes;
|
mod tiled_panes;
|
||||||
|
|
||||||
|
pub use active_panes::*;
|
||||||
pub use alacritty_functions::*;
|
pub use alacritty_functions::*;
|
||||||
pub use floating_panes::*;
|
pub use floating_panes::*;
|
||||||
pub use grid::*;
|
pub use grid::*;
|
||||||
|
|
|
||||||
|
|
@ -655,6 +655,12 @@ impl Pane for TerminalPane {
|
||||||
fn mouse_scroll_down(&self, position: &Position) -> Option<String> {
|
fn mouse_scroll_down(&self, position: &Position) -> Option<String> {
|
||||||
self.grid.mouse_scroll_down_signal(position)
|
self.grid.mouse_scroll_down_signal(position)
|
||||||
}
|
}
|
||||||
|
fn focus_event(&self) -> Option<String> {
|
||||||
|
self.grid.focus_event()
|
||||||
|
}
|
||||||
|
fn unfocus_event(&self) -> Option<String> {
|
||||||
|
self.grid.unfocus_event()
|
||||||
|
}
|
||||||
fn get_line_number(&self) -> Option<usize> {
|
fn get_line_number(&self) -> Option<usize> {
|
||||||
// + 1 because the absolute position in the scrollback is 0 indexed and this should be 1 indexed
|
// + 1 because the absolute position in the scrollback is 0 indexed and this should be 1 indexed
|
||||||
Some(self.grid.absolute_position_in_scrollback() + 1)
|
Some(self.grid.absolute_position_in_scrollback() + 1)
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,12 @@ use crate::tab::{Pane, MIN_TERMINAL_HEIGHT, MIN_TERMINAL_WIDTH};
|
||||||
use tiled_pane_grid::{split, TiledPaneGrid};
|
use tiled_pane_grid::{split, TiledPaneGrid};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
os_input_output::ServerOsApi, output::Output, panes::PaneId, ui::boundaries::Boundaries,
|
os_input_output::ServerOsApi,
|
||||||
ui::pane_contents_and_ui::PaneContentsAndUi, ClientId,
|
output::Output,
|
||||||
|
panes::{ActivePanes, PaneId},
|
||||||
|
ui::boundaries::Boundaries,
|
||||||
|
ui::pane_contents_and_ui::PaneContentsAndUi,
|
||||||
|
ClientId,
|
||||||
};
|
};
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::collections::{BTreeMap, HashMap, HashSet};
|
use std::collections::{BTreeMap, HashMap, HashSet};
|
||||||
|
|
@ -66,7 +70,7 @@ pub struct TiledPanes {
|
||||||
default_mode_info: ModeInfo,
|
default_mode_info: ModeInfo,
|
||||||
style: Style,
|
style: Style,
|
||||||
session_is_mirrored: bool,
|
session_is_mirrored: bool,
|
||||||
active_panes: HashMap<ClientId, PaneId>,
|
active_panes: ActivePanes,
|
||||||
draw_pane_frames: bool,
|
draw_pane_frames: bool,
|
||||||
panes_to_hide: HashSet<PaneId>,
|
panes_to_hide: HashSet<PaneId>,
|
||||||
fullscreen_is_active: bool,
|
fullscreen_is_active: bool,
|
||||||
|
|
@ -99,7 +103,7 @@ impl TiledPanes {
|
||||||
default_mode_info,
|
default_mode_info,
|
||||||
style,
|
style,
|
||||||
session_is_mirrored,
|
session_is_mirrored,
|
||||||
active_panes: HashMap::new(),
|
active_panes: ActivePanes::new(&os_api),
|
||||||
draw_pane_frames,
|
draw_pane_frames,
|
||||||
panes_to_hide: HashSet::new(),
|
panes_to_hide: HashSet::new(),
|
||||||
fullscreen_is_active: false,
|
fullscreen_is_active: false,
|
||||||
|
|
@ -330,18 +334,20 @@ impl TiledPanes {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn focus_pane(&mut self, pane_id: PaneId, client_id: ClientId) {
|
pub fn focus_pane(&mut self, pane_id: PaneId, client_id: ClientId) {
|
||||||
self.active_panes.insert(client_id, pane_id);
|
self.active_panes
|
||||||
|
.insert(client_id, pane_id, &mut self.panes);
|
||||||
if self.session_is_mirrored {
|
if self.session_is_mirrored {
|
||||||
// move all clients
|
// move all clients
|
||||||
let connected_clients: Vec<ClientId> =
|
let connected_clients: Vec<ClientId> =
|
||||||
self.connected_clients.borrow().iter().copied().collect();
|
self.connected_clients.borrow().iter().copied().collect();
|
||||||
for client_id in connected_clients {
|
for client_id in connected_clients {
|
||||||
self.active_panes.insert(client_id, pane_id);
|
self.active_panes
|
||||||
|
.insert(client_id, pane_id, &mut self.panes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn clear_active_panes(&mut self) {
|
pub fn clear_active_panes(&mut self) {
|
||||||
self.active_panes.clear();
|
self.active_panes.clear(&mut self.panes);
|
||||||
}
|
}
|
||||||
pub fn first_active_pane_id(&self) -> Option<PaneId> {
|
pub fn first_active_pane_id(&self) -> Option<PaneId> {
|
||||||
self.connected_clients
|
self.connected_clients
|
||||||
|
|
@ -595,7 +601,8 @@ impl TiledPanes {
|
||||||
);
|
);
|
||||||
let next_active_pane_id = pane_grid.next_selectable_pane_id(&active_pane_id);
|
let next_active_pane_id = pane_grid.next_selectable_pane_id(&active_pane_id);
|
||||||
for client_id in connected_clients {
|
for client_id in connected_clients {
|
||||||
self.active_panes.insert(client_id, next_active_pane_id);
|
self.active_panes
|
||||||
|
.insert(client_id, next_active_pane_id, &mut self.panes);
|
||||||
}
|
}
|
||||||
self.set_pane_active_at(next_active_pane_id);
|
self.set_pane_active_at(next_active_pane_id);
|
||||||
}
|
}
|
||||||
|
|
@ -611,7 +618,8 @@ impl TiledPanes {
|
||||||
);
|
);
|
||||||
let next_active_pane_id = pane_grid.previous_selectable_pane_id(&active_pane_id);
|
let next_active_pane_id = pane_grid.previous_selectable_pane_id(&active_pane_id);
|
||||||
for client_id in connected_clients {
|
for client_id in connected_clients {
|
||||||
self.active_panes.insert(client_id, next_active_pane_id);
|
self.active_panes
|
||||||
|
.insert(client_id, next_active_pane_id, &mut self.panes);
|
||||||
}
|
}
|
||||||
self.set_pane_active_at(next_active_pane_id);
|
self.set_pane_active_at(next_active_pane_id);
|
||||||
}
|
}
|
||||||
|
|
@ -967,21 +975,22 @@ impl TiledPanes {
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(cid, pid)| (*cid, *pid))
|
.map(|(cid, pid)| (*cid, *pid))
|
||||||
.collect();
|
.collect();
|
||||||
match self
|
let next_active_pane_id = self
|
||||||
.panes
|
.panes
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|(p_id, _)| !self.panes_to_hide.contains(p_id))
|
.filter(|(p_id, _)| !self.panes_to_hide.contains(p_id))
|
||||||
.find(|(p_id, p)| **p_id != pane_id && p.selectable())
|
.find(|(p_id, p)| **p_id != pane_id && p.selectable())
|
||||||
.map(|(p_id, _p)| p_id)
|
.map(|(p_id, _p)| *p_id);
|
||||||
{
|
match next_active_pane_id {
|
||||||
Some(next_active_pane) => {
|
Some(next_active_pane) => {
|
||||||
for (client_id, active_pane_id) in active_panes {
|
for (client_id, active_pane_id) in active_panes {
|
||||||
if active_pane_id == pane_id {
|
if active_pane_id == pane_id {
|
||||||
self.active_panes.insert(client_id, *next_active_pane);
|
self.active_panes
|
||||||
|
.insert(client_id, next_active_pane, &mut self.panes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
None => self.active_panes.clear(),
|
None => self.active_panes.clear(&mut self.panes),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn extract_pane(&mut self, pane_id: PaneId) -> Option<Box<dyn Pane>> {
|
pub fn extract_pane(&mut self, pane_id: PaneId) -> Option<Box<dyn Pane>> {
|
||||||
|
|
@ -1004,7 +1013,7 @@ impl TiledPanes {
|
||||||
self.panes.remove(&pane_id);
|
self.panes.remove(&pane_id);
|
||||||
// this is a bit of a roundabout way to say: this is the last pane and so the tab
|
// this is a bit of a roundabout way to say: this is the last pane and so the tab
|
||||||
// should be destroyed
|
// should be destroyed
|
||||||
self.active_panes.clear();
|
self.active_panes.clear(&mut self.panes);
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1141,6 +1150,12 @@ impl TiledPanes {
|
||||||
pub fn remove_from_hidden_panels(&mut self, pid: PaneId) {
|
pub fn remove_from_hidden_panels(&mut self, pid: PaneId) {
|
||||||
self.panes_to_hide.remove(&pid);
|
self.panes_to_hide.remove(&pid);
|
||||||
}
|
}
|
||||||
|
pub fn unfocus_all_panes(&mut self) {
|
||||||
|
self.active_panes.unfocus_all_panes(&mut self.panes);
|
||||||
|
}
|
||||||
|
pub fn focus_all_panes(&mut self) {
|
||||||
|
self.active_panes.focus_all_panes(&mut self.panes);
|
||||||
|
}
|
||||||
fn move_clients_between_panes(&mut self, from_pane_id: PaneId, to_pane_id: PaneId) {
|
fn move_clients_between_panes(&mut self, from_pane_id: PaneId, to_pane_id: PaneId) {
|
||||||
let clients_in_pane: Vec<ClientId> = self
|
let clients_in_pane: Vec<ClientId> = self
|
||||||
.active_panes
|
.active_panes
|
||||||
|
|
@ -1149,8 +1164,9 @@ impl TiledPanes {
|
||||||
.map(|(cid, _pid)| *cid)
|
.map(|(cid, _pid)| *cid)
|
||||||
.collect();
|
.collect();
|
||||||
for client_id in clients_in_pane {
|
for client_id in clients_in_pane {
|
||||||
self.active_panes.remove(&client_id);
|
self.active_panes.remove(&client_id, &mut self.panes);
|
||||||
self.active_panes.insert(client_id, to_pane_id);
|
self.active_panes
|
||||||
|
.insert(client_id, to_pane_id, &mut self.panes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -330,6 +330,12 @@ pub trait Pane {
|
||||||
fn mouse_scroll_down(&self, _position: &Position) -> Option<String> {
|
fn mouse_scroll_down(&self, _position: &Position) -> Option<String> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
fn focus_event(&self) -> Option<String> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
fn unfocus_event(&self) -> Option<String> {
|
||||||
|
None
|
||||||
|
}
|
||||||
fn get_line_number(&self) -> Option<usize> {
|
fn get_line_number(&self) -> Option<usize> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
@ -429,6 +435,7 @@ impl Tab {
|
||||||
session_is_mirrored,
|
session_is_mirrored,
|
||||||
default_mode_info.clone(),
|
default_mode_info.clone(),
|
||||||
style,
|
style,
|
||||||
|
os_api.clone(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let clipboard_provider = match copy_options.command {
|
let clipboard_provider = match copy_options.command {
|
||||||
|
|
@ -751,7 +758,7 @@ impl Tab {
|
||||||
self.should_clear_display_before_rendering = true;
|
self.should_clear_display_before_rendering = true;
|
||||||
self.tiled_panes
|
self.tiled_panes
|
||||||
.focus_pane(focused_floating_pane_id, client_id);
|
.focus_pane(focused_floating_pane_id, client_id);
|
||||||
self.floating_panes.toggle_show_panes(false);
|
self.hide_floating_panes();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if let Some(focused_pane_id) = self.tiled_panes.focused_pane_id(client_id) {
|
} else if let Some(focused_pane_id) = self.tiled_panes.focused_pane_id(client_id) {
|
||||||
|
|
@ -767,7 +774,7 @@ impl Tab {
|
||||||
self.floating_panes
|
self.floating_panes
|
||||||
.add_pane(focused_pane_id, embedded_pane_to_float);
|
.add_pane(focused_pane_id, embedded_pane_to_float);
|
||||||
self.floating_panes.focus_pane(focused_pane_id, client_id);
|
self.floating_panes.focus_pane(focused_pane_id, client_id);
|
||||||
self.floating_panes.toggle_show_panes(true);
|
self.show_floating_panes();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -779,10 +786,10 @@ impl Tab {
|
||||||
default_shell: Option<TerminalAction>,
|
default_shell: Option<TerminalAction>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
if self.floating_panes.panes_are_visible() {
|
if self.floating_panes.panes_are_visible() {
|
||||||
self.floating_panes.toggle_show_panes(false);
|
self.hide_floating_panes();
|
||||||
self.set_force_render();
|
self.set_force_render();
|
||||||
} else {
|
} else {
|
||||||
self.floating_panes.toggle_show_panes(true);
|
self.show_floating_panes();
|
||||||
match self.floating_panes.first_floating_pane_id() {
|
match self.floating_panes.first_floating_pane_id() {
|
||||||
Some(first_floating_pane_id) => {
|
Some(first_floating_pane_id) => {
|
||||||
if !self.floating_panes.active_panes_contain(&client_id) {
|
if !self.floating_panes.active_panes_contain(&client_id) {
|
||||||
|
|
@ -819,8 +826,8 @@ impl Tab {
|
||||||
let err_context = || format!("failed to create new pane with id {pid:?}");
|
let err_context = || format!("failed to create new pane with id {pid:?}");
|
||||||
|
|
||||||
match should_float {
|
match should_float {
|
||||||
Some(true) => self.floating_panes.toggle_show_panes(true),
|
Some(true) => self.show_floating_panes(),
|
||||||
Some(false) => self.floating_panes.toggle_show_panes(false),
|
Some(false) => self.hide_floating_panes(),
|
||||||
None => {},
|
None => {},
|
||||||
};
|
};
|
||||||
self.close_down_to_max_terminals()
|
self.close_down_to_max_terminals()
|
||||||
|
|
@ -1759,7 +1766,7 @@ impl Tab {
|
||||||
let closed_pane = self.floating_panes.remove_pane(id);
|
let closed_pane = self.floating_panes.remove_pane(id);
|
||||||
self.floating_panes.move_clients_out_of_pane(id);
|
self.floating_panes.move_clients_out_of_pane(id);
|
||||||
if !self.floating_panes.has_panes() {
|
if !self.floating_panes.has_panes() {
|
||||||
self.floating_panes.toggle_show_panes(false);
|
self.hide_floating_panes();
|
||||||
}
|
}
|
||||||
self.set_force_render();
|
self.set_force_render();
|
||||||
self.floating_panes.set_force_render();
|
self.floating_panes.set_force_render();
|
||||||
|
|
@ -2206,7 +2213,7 @@ impl Tab {
|
||||||
self.tiled_panes.focus_pane(clicked_pane, client_id);
|
self.tiled_panes.focus_pane(clicked_pane, client_id);
|
||||||
self.set_pane_active_at(clicked_pane);
|
self.set_pane_active_at(clicked_pane);
|
||||||
if self.floating_panes.panes_are_visible() {
|
if self.floating_panes.panes_are_visible() {
|
||||||
self.floating_panes.toggle_show_panes(false);
|
self.hide_floating_panes();
|
||||||
self.set_force_render();
|
self.set_force_render();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2711,6 +2718,19 @@ impl Tab {
|
||||||
active_pane.clear_search();
|
active_pane.clear_search();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn show_floating_panes(&mut self) {
|
||||||
|
// this function is to be preferred to directly invoking floating_panes.toggle_show_panes(true)
|
||||||
|
self.floating_panes.toggle_show_panes(true);
|
||||||
|
self.tiled_panes.unfocus_all_panes();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hide_floating_panes(&mut self) {
|
||||||
|
// this function is to be preferred to directly invoking
|
||||||
|
// floating_panes.toggle_show_panes(false)
|
||||||
|
self.floating_panes.toggle_show_panes(false);
|
||||||
|
self.tiled_panes.focus_all_panes();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
---
|
||||||
|
source: zellij-server/src/tab/./unit/tab_integration_tests.rs
|
||||||
|
assertion_line: 2657
|
||||||
|
expression: "format!(\"{:?}\", * tty_stdin_bytes.lock().unwrap())"
|
||||||
|
---
|
||||||
|
{2: [27, 91, 73, 27, 91, 79, 27, 91, 73], 3: [27, 91, 79]}
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
---
|
||||||
|
source: zellij-server/src/tab/./unit/tab_integration_tests.rs
|
||||||
|
assertion_line: 2614
|
||||||
|
expression: "format!(\"{:?}\", * tty_stdin_bytes.lock().unwrap())"
|
||||||
|
---
|
||||||
|
{1: [27, 91, 73, 27, 91, 79, 27, 91, 73], 2: [27, 91, 79]}
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
---
|
||||||
|
source: zellij-server/src/tab/./unit/tab_integration_tests.rs
|
||||||
|
assertion_line: 2615
|
||||||
|
expression: "format!(\"{:?}\", * tty_stdin_bytes.lock().unwrap())"
|
||||||
|
---
|
||||||
|
{2: [27, 91, 79], 1: [27, 91, 73, 27, 91, 79, 27, 91, 73]}
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
---
|
||||||
|
source: zellij-server/src/tab/./unit/tab_integration_tests.rs
|
||||||
|
assertion_line: 2744
|
||||||
|
expression: "format!(\"{:?}\", * tty_stdin_bytes.lock().unwrap())"
|
||||||
|
---
|
||||||
|
{1: [27, 91, 73], 3: [27, 91, 79]}
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
---
|
||||||
|
source: zellij-server/src/tab/./unit/tab_integration_tests.rs
|
||||||
|
assertion_line: 2701
|
||||||
|
expression: "format!(\"{:?}\", * tty_stdin_bytes.lock().unwrap())"
|
||||||
|
---
|
||||||
|
{1: [27, 91, 79], 3: [27, 91, 73]}
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
---
|
||||||
|
source: zellij-server/src/tab/./unit/tab_integration_tests.rs
|
||||||
|
assertion_line: 2701
|
||||||
|
expression: "format!(\"{:?}\", * tty_stdin_bytes.lock().unwrap())"
|
||||||
|
---
|
||||||
|
{1: [27, 91, 79], 3: [27, 91, 73]}
|
||||||
|
|
@ -22,7 +22,7 @@ use crate::pty_writer::PtyWriteInstruction;
|
||||||
use zellij_utils::channels::{self, ChannelWithContext, SenderWithContext};
|
use zellij_utils::channels::{self, ChannelWithContext, SenderWithContext};
|
||||||
|
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{BTreeMap, HashMap, HashSet};
|
||||||
use std::os::unix::io::RawFd;
|
use std::os::unix::io::RawFd;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
|
@ -33,9 +33,10 @@ use zellij_utils::{
|
||||||
ipc::{ClientToServerMsg, ServerToClientMsg},
|
ipc::{ClientToServerMsg, ServerToClientMsg},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Default)]
|
||||||
struct FakeInputOutput {
|
struct FakeInputOutput {
|
||||||
file_dumps: Arc<Mutex<HashMap<String, String>>>,
|
file_dumps: Arc<Mutex<HashMap<String, String>>>,
|
||||||
|
pub tty_stdin_bytes: Arc<Mutex<BTreeMap<u32, Vec<u8>>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ServerOsApi for FakeInputOutput {
|
impl ServerOsApi for FakeInputOutput {
|
||||||
|
|
@ -57,8 +58,14 @@ impl ServerOsApi for FakeInputOutput {
|
||||||
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, _id: u32, _buf: &[u8]) -> Result<usize> {
|
fn write_to_tty_stdin(&self, id: u32, buf: &[u8]) -> Result<usize> {
|
||||||
unimplemented!()
|
self.tty_stdin_bytes
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.entry(id)
|
||||||
|
.or_insert_with(|| vec![])
|
||||||
|
.extend_from_slice(buf);
|
||||||
|
Ok(buf.len())
|
||||||
}
|
}
|
||||||
fn tcdrain(&self, _id: u32) -> Result<()> {
|
fn tcdrain(&self, _id: u32) -> Result<()> {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
|
|
@ -181,9 +188,57 @@ fn create_new_tab(size: Size, default_mode: ModeInfo) -> Tab {
|
||||||
let index = 0;
|
let index = 0;
|
||||||
let position = 0;
|
let position = 0;
|
||||||
let name = String::new();
|
let name = String::new();
|
||||||
let os_api = Box::new(FakeInputOutput {
|
let os_api = Box::new(FakeInputOutput::default());
|
||||||
file_dumps: Arc::new(Mutex::new(HashMap::new())),
|
let senders = ThreadSenders::default().silently_fail_on_send();
|
||||||
});
|
let max_panes = None;
|
||||||
|
let mode_info = default_mode;
|
||||||
|
let style = Style::default();
|
||||||
|
let draw_pane_frames = true;
|
||||||
|
let client_id = 1;
|
||||||
|
let session_is_mirrored = true;
|
||||||
|
let mut connected_clients = HashSet::new();
|
||||||
|
connected_clients.insert(client_id);
|
||||||
|
let connected_clients = Rc::new(RefCell::new(connected_clients));
|
||||||
|
let character_cell_info = Rc::new(RefCell::new(None));
|
||||||
|
let terminal_emulator_colors = Rc::new(RefCell::new(Palette::default()));
|
||||||
|
let copy_options = CopyOptions::default();
|
||||||
|
let terminal_emulator_color_codes = Rc::new(RefCell::new(HashMap::new()));
|
||||||
|
let sixel_image_store = Rc::new(RefCell::new(SixelImageStore::default()));
|
||||||
|
let mut tab = Tab::new(
|
||||||
|
index,
|
||||||
|
position,
|
||||||
|
name,
|
||||||
|
size,
|
||||||
|
character_cell_info,
|
||||||
|
sixel_image_store,
|
||||||
|
os_api,
|
||||||
|
senders,
|
||||||
|
max_panes,
|
||||||
|
style,
|
||||||
|
mode_info,
|
||||||
|
draw_pane_frames,
|
||||||
|
connected_clients,
|
||||||
|
session_is_mirrored,
|
||||||
|
client_id,
|
||||||
|
copy_options,
|
||||||
|
terminal_emulator_colors,
|
||||||
|
terminal_emulator_color_codes,
|
||||||
|
);
|
||||||
|
tab.apply_layout(PaneLayout::default(), vec![(1, None)], index, client_id)
|
||||||
|
.unwrap();
|
||||||
|
tab
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_new_tab_with_os_api(
|
||||||
|
size: Size,
|
||||||
|
default_mode: ModeInfo,
|
||||||
|
os_api: &Box<FakeInputOutput>,
|
||||||
|
) -> Tab {
|
||||||
|
set_session_name("test".into());
|
||||||
|
let index = 0;
|
||||||
|
let position = 0;
|
||||||
|
let name = String::new();
|
||||||
|
let os_api = os_api.clone();
|
||||||
let senders = ThreadSenders::default().silently_fail_on_send();
|
let senders = ThreadSenders::default().silently_fail_on_send();
|
||||||
let max_panes = None;
|
let max_panes = None;
|
||||||
let mode_info = default_mode;
|
let mode_info = default_mode;
|
||||||
|
|
@ -229,9 +284,7 @@ fn create_new_tab_with_layout(size: Size, default_mode: ModeInfo, layout: &str)
|
||||||
let index = 0;
|
let index = 0;
|
||||||
let position = 0;
|
let position = 0;
|
||||||
let name = String::new();
|
let name = String::new();
|
||||||
let os_api = Box::new(FakeInputOutput {
|
let os_api = Box::new(FakeInputOutput::default());
|
||||||
file_dumps: Arc::new(Mutex::new(HashMap::new())),
|
|
||||||
});
|
|
||||||
let senders = ThreadSenders::default().silently_fail_on_send();
|
let senders = ThreadSenders::default().silently_fail_on_send();
|
||||||
let max_panes = None;
|
let max_panes = None;
|
||||||
let mode_info = default_mode;
|
let mode_info = default_mode;
|
||||||
|
|
@ -289,9 +342,7 @@ fn create_new_tab_with_mock_pty_writer(
|
||||||
let index = 0;
|
let index = 0;
|
||||||
let position = 0;
|
let position = 0;
|
||||||
let name = String::new();
|
let name = String::new();
|
||||||
let os_api = Box::new(FakeInputOutput {
|
let os_api = Box::new(FakeInputOutput::default());
|
||||||
file_dumps: Arc::new(Mutex::new(HashMap::new())),
|
|
||||||
});
|
|
||||||
let mut senders = ThreadSenders::default().silently_fail_on_send();
|
let mut senders = ThreadSenders::default().silently_fail_on_send();
|
||||||
senders.replace_to_pty_writer(mock_pty_writer);
|
senders.replace_to_pty_writer(mock_pty_writer);
|
||||||
let max_panes = None;
|
let max_panes = None;
|
||||||
|
|
@ -343,9 +394,7 @@ fn create_new_tab_with_sixel_support(
|
||||||
let index = 0;
|
let index = 0;
|
||||||
let position = 0;
|
let position = 0;
|
||||||
let name = String::new();
|
let name = String::new();
|
||||||
let os_api = Box::new(FakeInputOutput {
|
let os_api = Box::new(FakeInputOutput::default());
|
||||||
file_dumps: Arc::new(Mutex::new(HashMap::new())),
|
|
||||||
});
|
|
||||||
let senders = ThreadSenders::default().silently_fail_on_send();
|
let senders = ThreadSenders::default().silently_fail_on_send();
|
||||||
let max_panes = None;
|
let max_panes = None;
|
||||||
let mode_info = ModeInfo::default();
|
let mode_info = ModeInfo::default();
|
||||||
|
|
@ -490,6 +539,7 @@ fn dump_screen() {
|
||||||
let map = Arc::new(Mutex::new(HashMap::new()));
|
let map = Arc::new(Mutex::new(HashMap::new()));
|
||||||
tab.os_api = Box::new(FakeInputOutput {
|
tab.os_api = Box::new(FakeInputOutput {
|
||||||
file_dumps: map.clone(),
|
file_dumps: map.clone(),
|
||||||
|
..Default::default()
|
||||||
});
|
});
|
||||||
let new_pane_id = PaneId::Terminal(2);
|
let new_pane_id = PaneId::Terminal(2);
|
||||||
tab.new_pane(new_pane_id, None, None, Some(client_id))
|
tab.new_pane(new_pane_id, None, None, Some(client_id))
|
||||||
|
|
@ -2522,3 +2572,165 @@ fn pane_faux_scrolling_in_alternate_mode() {
|
||||||
|
|
||||||
assert_eq!(pty_instruction_bus.clone_output(), expected);
|
assert_eq!(pty_instruction_bus.clone_output(), expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn move_pane_focus_sends_tty_csi_event() {
|
||||||
|
let size = Size {
|
||||||
|
cols: 121,
|
||||||
|
rows: 20,
|
||||||
|
};
|
||||||
|
let client_id = 1;
|
||||||
|
let tty_stdin_bytes = Arc::new(Mutex::new(BTreeMap::new()));
|
||||||
|
let os_api = Box::new(FakeInputOutput {
|
||||||
|
tty_stdin_bytes: tty_stdin_bytes.clone(),
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
let mut tab = create_new_tab_with_os_api(size, ModeInfo::default(), &os_api);
|
||||||
|
let new_pane_id_1 = PaneId::Terminal(2);
|
||||||
|
tab.new_pane(new_pane_id_1, None, None, Some(client_id))
|
||||||
|
.unwrap();
|
||||||
|
tab.handle_pty_bytes(
|
||||||
|
1,
|
||||||
|
// subscribe to focus events
|
||||||
|
Vec::from("\u{1b}[?1004h".as_bytes()),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
tab.handle_pty_bytes(
|
||||||
|
2,
|
||||||
|
// subscribe to focus events
|
||||||
|
Vec::from("\u{1b}[?1004h".as_bytes()),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
tab.move_focus_left(client_id);
|
||||||
|
assert_snapshot!(format!("{:?}", *tty_stdin_bytes.lock().unwrap()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn move_floating_pane_focus_sends_tty_csi_event() {
|
||||||
|
let size = Size {
|
||||||
|
cols: 121,
|
||||||
|
rows: 20,
|
||||||
|
};
|
||||||
|
let client_id = 1;
|
||||||
|
let tty_stdin_bytes = Arc::new(Mutex::new(BTreeMap::new()));
|
||||||
|
let os_api = Box::new(FakeInputOutput {
|
||||||
|
tty_stdin_bytes: tty_stdin_bytes.clone(),
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
let mut tab = create_new_tab_with_os_api(size, ModeInfo::default(), &os_api);
|
||||||
|
let new_pane_id_1 = PaneId::Terminal(2);
|
||||||
|
let new_pane_id_2 = PaneId::Terminal(3);
|
||||||
|
|
||||||
|
tab.toggle_floating_panes(client_id, None).unwrap();
|
||||||
|
tab.new_pane(new_pane_id_1, None, None, Some(client_id))
|
||||||
|
.unwrap();
|
||||||
|
tab.new_pane(new_pane_id_2, None, None, Some(client_id))
|
||||||
|
.unwrap();
|
||||||
|
tab.handle_pty_bytes(
|
||||||
|
1,
|
||||||
|
// subscribe to focus events
|
||||||
|
Vec::from("\u{1b}[?1004h".as_bytes()),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
tab.handle_pty_bytes(
|
||||||
|
2,
|
||||||
|
// subscribe to focus events
|
||||||
|
Vec::from("\u{1b}[?1004h".as_bytes()),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
tab.handle_pty_bytes(
|
||||||
|
3,
|
||||||
|
// subscribe to focus events
|
||||||
|
Vec::from("\u{1b}[?1004h".as_bytes()),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
tab.move_focus_left(client_id);
|
||||||
|
assert_snapshot!(format!("{:?}", *tty_stdin_bytes.lock().unwrap()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn toggle_floating_panes_on_sends_tty_csi_event() {
|
||||||
|
let size = Size {
|
||||||
|
cols: 121,
|
||||||
|
rows: 20,
|
||||||
|
};
|
||||||
|
let client_id = 1;
|
||||||
|
let tty_stdin_bytes = Arc::new(Mutex::new(BTreeMap::new()));
|
||||||
|
let os_api = Box::new(FakeInputOutput {
|
||||||
|
tty_stdin_bytes: tty_stdin_bytes.clone(),
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
let mut tab = create_new_tab_with_os_api(size, ModeInfo::default(), &os_api);
|
||||||
|
let new_pane_id_1 = PaneId::Terminal(2);
|
||||||
|
let new_pane_id_2 = PaneId::Terminal(3);
|
||||||
|
|
||||||
|
tab.toggle_floating_panes(client_id, None).unwrap();
|
||||||
|
tab.new_pane(new_pane_id_1, None, None, Some(client_id))
|
||||||
|
.unwrap();
|
||||||
|
tab.new_pane(new_pane_id_2, None, None, Some(client_id))
|
||||||
|
.unwrap();
|
||||||
|
tab.toggle_floating_panes(client_id, None).unwrap();
|
||||||
|
tab.handle_pty_bytes(
|
||||||
|
1,
|
||||||
|
// subscribe to focus events
|
||||||
|
Vec::from("\u{1b}[?1004h".as_bytes()),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
tab.handle_pty_bytes(
|
||||||
|
2,
|
||||||
|
// subscribe to focus events
|
||||||
|
Vec::from("\u{1b}[?1004h".as_bytes()),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
tab.handle_pty_bytes(
|
||||||
|
3,
|
||||||
|
// subscribe to focus events
|
||||||
|
Vec::from("\u{1b}[?1004h".as_bytes()),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
tab.toggle_floating_panes(client_id, None).unwrap();
|
||||||
|
assert_snapshot!(format!("{:?}", *tty_stdin_bytes.lock().unwrap()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn toggle_floating_panes_off_sends_tty_csi_event() {
|
||||||
|
let size = Size {
|
||||||
|
cols: 121,
|
||||||
|
rows: 20,
|
||||||
|
};
|
||||||
|
let client_id = 1;
|
||||||
|
let tty_stdin_bytes = Arc::new(Mutex::new(BTreeMap::new()));
|
||||||
|
let os_api = Box::new(FakeInputOutput {
|
||||||
|
tty_stdin_bytes: tty_stdin_bytes.clone(),
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
let mut tab = create_new_tab_with_os_api(size, ModeInfo::default(), &os_api);
|
||||||
|
let new_pane_id_1 = PaneId::Terminal(2);
|
||||||
|
let new_pane_id_2 = PaneId::Terminal(3);
|
||||||
|
|
||||||
|
tab.toggle_floating_panes(client_id, None).unwrap();
|
||||||
|
tab.new_pane(new_pane_id_1, None, None, Some(client_id))
|
||||||
|
.unwrap();
|
||||||
|
tab.new_pane(new_pane_id_2, None, None, Some(client_id))
|
||||||
|
.unwrap();
|
||||||
|
tab.handle_pty_bytes(
|
||||||
|
1,
|
||||||
|
// subscribe to focus events
|
||||||
|
Vec::from("\u{1b}[?1004h".as_bytes()),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
tab.handle_pty_bytes(
|
||||||
|
2,
|
||||||
|
// subscribe to focus events
|
||||||
|
Vec::from("\u{1b}[?1004h".as_bytes()),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
tab.handle_pty_bytes(
|
||||||
|
3,
|
||||||
|
// subscribe to focus events
|
||||||
|
Vec::from("\u{1b}[?1004h".as_bytes()),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
tab.toggle_floating_panes(client_id, None).unwrap();
|
||||||
|
assert_snapshot!(format!("{:?}", *tty_stdin_bytes.lock().unwrap()));
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue