diff options
Diffstat (limited to 'zellij-server/src/screen.rs')
-rw-r--r-- | zellij-server/src/screen.rs | 396 |
1 files changed, 234 insertions, 162 deletions
diff --git a/zellij-server/src/screen.rs b/zellij-server/src/screen.rs index e3323b41e..b302db4be 100644 --- a/zellij-server/src/screen.rs +++ b/zellij-server/src/screen.rs @@ -6,6 +6,7 @@ use std::os::unix::io::RawFd; use std::rc::Rc; use std::str; +use zellij_utils::errors::prelude::*; use zellij_utils::input::options::Clipboard; use zellij_utils::pane_size::{Size, SizeInPixels}; use zellij_utils::{input::command::TerminalAction, input::layout::Layout, position::Position}; @@ -419,13 +420,20 @@ impl Screen { }, } } + /// A helper function to switch to a new tab at specified position. - fn switch_active_tab(&mut self, new_tab_pos: usize, client_id: ClientId) { + fn switch_active_tab(&mut self, new_tab_pos: usize, client_id: ClientId) -> Result<()> { + let err_context = || { + format!( + "Failed to switch to active tab at position {new_tab_pos} for client id: {client_id:?}" + ) + }; + if let Some(new_tab) = self.tabs.values().find(|t| t.position == new_tab_pos) { if let Some(current_tab) = self.get_active_tab(client_id) { // If new active tab is same as the current one, do nothing. if current_tab.position == new_tab_pos { - return; + return Ok(()); } let current_tab_index = current_tab.index; @@ -454,27 +462,29 @@ impl Screen { log::error!("Tab index: {:?} not found", current_tab_index); } - self.update_tabs(); - self.render(); + self.update_tabs().with_context(err_context)?; + return self.render().with_context(err_context); } else { - log::error!("Active tab not found for client_id: {:?}", client_id); + log::error!("Active tab not found for client id: {client_id:?}"); } } + Ok(()) } /// Sets this [`Screen`]'s active [`Tab`] to the next tab. - pub fn switch_tab_next(&mut self, client_id: ClientId) { + pub fn switch_tab_next(&mut self, client_id: ClientId) -> Result<()> { if let Some(active_tab) = self.get_active_tab(client_id) { let active_tab_pos = active_tab.position; let new_tab_pos = (active_tab_pos + 1) % self.tabs.len(); - self.switch_active_tab(new_tab_pos, client_id); + self.switch_active_tab(new_tab_pos, client_id) } else { - log::error!("Active tab not found for client_id: {:?}", client_id); + log::error!("Active tab not found for client id: {client_id:?}"); + Ok(()) } } /// Sets this [`Screen`]'s active [`Tab`] to the previous tab. - pub fn switch_tab_prev(&mut self, client_id: ClientId) { + pub fn switch_tab_prev(&mut self, client_id: ClientId) -> Result<()> { if let Some(active_tab) = self.get_active_tab(client_id) { let active_tab_pos = active_tab.position; let new_tab_pos = if active_tab_pos == 0 { @@ -483,18 +493,20 @@ impl Screen { active_tab_pos - 1 }; - self.switch_active_tab(new_tab_pos, client_id); + self.switch_active_tab(new_tab_pos, client_id) } else { - log::error!("Active tab not found for client_id: {:?}", client_id); + log::error!("Active tab not found for client id: {client_id:?}"); + Ok(()) } } - pub fn go_to_tab(&mut self, tab_index: usize, client_id: ClientId) { - self.switch_active_tab(tab_index - 1, client_id); + pub fn go_to_tab(&mut self, tab_index: usize, client_id: ClientId) -> Result<()> { + self.switch_active_tab(tab_index - 1, client_id) } - fn close_tab_at_index(&mut self, tab_index: usize) { - let mut tab_to_close = self.tabs.remove(&tab_index).unwrap(); + fn close_tab_at_index(&mut self, tab_index: usize) -> Result<()> { + let err_context = || format!("failed to close tab at index {tab_index:?}"); + let mut tab_to_close = self.tabs.remove(&tab_index).with_context(err_context)?; let pane_ids = tab_to_close.get_all_pane_ids(); // below we don't check the result of sending the CloseTab instruction to the pty thread // because this might be happening when the app is closing, at which point the pty thread @@ -502,13 +514,13 @@ impl Screen { self.bus .senders .send_to_pty(PtyInstruction::CloseTab(pane_ids)) - .unwrap(); + .with_context(err_context)?; if self.tabs.is_empty() { self.active_tab_indices.clear(); self.bus .senders .send_to_server(ServerInstruction::Render(None)) - .unwrap(); + .with_context(err_context) } else { let client_mode_infos_in_closed_tab = tab_to_close.drain_connected_clients(None); self.move_clients_from_closed_tab(client_mode_infos_in_closed_tab); @@ -523,25 +535,33 @@ impl Screen { t.position -= 1; } } - self.update_tabs(); - self.render(); + self.update_tabs().with_context(err_context)?; + self.render().with_context(err_context) } } // Closes the client_id's focused tab - pub fn close_tab(&mut self, client_id: ClientId) { - let active_tab_index = *self.active_tab_indices.get(&client_id).unwrap(); - self.close_tab_at_index(active_tab_index); + pub fn close_tab(&mut self, client_id: ClientId) -> Result<()> { + let err_context = || format!("failed to close tab for client {client_id:?}"); + + let active_tab_index = *self + .active_tab_indices + .get(&client_id) + .with_context(err_context)?; + self.close_tab_at_index(active_tab_index) + .with_context(err_context) } - pub fn resize_to_screen(&mut self, new_screen_size: Size) { + pub fn resize_to_screen(&mut self, new_screen_size: Size) -> Result<()> { self.size = new_screen_size; for tab in self.tabs.values_mut() { tab.resize_whole_tab(new_screen_size); tab.set_force_render(); } - self.render(); + self.render() + .context("failed to resize to screen size: {new_screen_size:#?}") } + pub fn update_pixel_dimensions(&mut self, pixel_dimensions: PixelDimensions) { self.pixel_dimensions.merge(pixel_dimensions); if let Some(character_cell_size) = self.pixel_dimensions.character_cell_size { @@ -556,6 +576,7 @@ impl Screen { *self.character_cell_size.borrow_mut() = Some(character_cell_size); } } + pub fn update_terminal_background_color(&mut self, background_color_instruction: String) { if let Some(AnsiCode::RgbCode((r, g, b))) = xparse_color(background_color_instruction.as_bytes()) @@ -564,6 +585,7 @@ impl Screen { self.terminal_emulator_colors.borrow_mut().bg = bg_palette_color; } } + pub fn update_terminal_foreground_color(&mut self, foreground_color_instruction: String) { if let Some(AnsiCode::RgbCode((r, g, b))) = xparse_color(foreground_color_instruction.as_bytes()) @@ -572,6 +594,7 @@ impl Screen { self.terminal_emulator_colors.borrow_mut().fg = fg_palette_color; } } + pub fn update_terminal_color_registers(&mut self, color_registers: Vec<(usize, String)>) { let mut terminal_emulator_color_codes = self.terminal_emulator_color_codes.borrow_mut(); for (color_register, color_sequence) in color_registers { @@ -580,7 +603,9 @@ impl Screen { } /// Renders this [`Screen`], which amounts to rendering its active [`Tab`]. - pub fn render(&mut self) { + pub fn render(&mut self) -> Result<()> { + let err_context = || "failed to render screen".to_string(); + let mut output = Output::new( self.sixel_image_store.clone(), self.character_cell_size.clone(), @@ -597,13 +622,14 @@ impl Screen { } } for tab_index in tabs_to_close { - self.close_tab_at_index(tab_index); + self.close_tab_at_index(tab_index) + .with_context(err_context)?; } let serialized_output = output.serialize(); self.bus .senders .send_to_server(ServerInstruction::Render(Some(serialized_output))) - .unwrap(); + .with_context(err_context) } /// Returns a mutable reference to this [`Screen`]'s tabs. @@ -621,11 +647,20 @@ impl Screen { /// Returns an immutable reference to this [`Screen`]'s previous active [`Tab`]. /// Consumes the last entry in tab history. - pub fn get_previous_tab(&mut self, client_id: ClientId) -> Option<&Tab> { - match self.tab_history.get_mut(&client_id).unwrap().pop() { - Some(tab) => self.tabs.get(&tab), - None => None, - } + pub fn get_previous_tab(&mut self, client_id: ClientId) -> Result<Option<&Tab>> { + Ok( + match self + .tab_history + .get_mut(&client_id) + .with_context(|| { + format!("failed to retrieve tab history for client {client_id:?}") + })? + .pop() + { + Some(tab) => self.tabs.get(&tab), + None => None, + }, + ) } /// Returns a mutable reference to this [`Screen`]'s active [`Tab`]. @@ -648,7 +683,14 @@ impl Screen { /// Creates a new [`Tab`] in this [`Screen`], applying the specified [`Layout`] /// and switching to it. - pub fn new_tab(&mut self, layout: Layout, new_pids: Vec<RawFd>, client_id: ClientId) { + pub fn new_tab( + &mut self, + layout: Layout, + new_pids: Vec<RawFd>, + client_id: ClientId, + ) -> Result<()> { + let err_context = || format!("failed to create new tab for client {client_id:?}",); + let tab_index = self.get_new_tab_index(); let position = self.tabs.len(); let mut tab = Tab::new( @@ -658,7 +700,11 @@ impl Screen { self.size, self.character_cell_size.clone(), self.sixel_image_store.clone(), - self.bus.os_input.as_ref().unwrap().clone(), + self.bus + .os_input + .as_ref() + .with_context(|| format!("failed to create new tab for client {client_id:?}"))? + .clone(), self.bus.senders.clone(), self.max_panes, self.style, @@ -699,14 +745,14 @@ impl Screen { self.tabs.insert(tab_index, tab); if !self.active_tab_indices.contains_key(&client_id) { // this means this is a new client and we need to add it to our state properly - self.add_client(client_id); + self.add_client(client_id).with_context(err_context)?; } - self.update_tabs(); + self.update_tabs().with_context(err_context)?; - self.render(); + self.render().with_context(err_context) } - pub fn add_client(&mut self, client_id: ClientId) { + pub fn add_client(&mut self, client_id: ClientId) -> Result<()> { let mut tab_history = vec![]; if let Some((_first_client, first_tab_history)) = self.tab_history.iter().next() { tab_history = first_tab_history.clone(); @@ -729,10 +775,12 @@ impl Screen { self.tab_history.insert(client_id, tab_history); self.tabs .get_mut(&tab_index) - .unwrap_or_else(|| panic!("Failed to attach client to tab with index {tab_index}")) + .with_context(|| format!("Failed to attach client to tab with index {tab_index}"))? .add_client(client_id, None); + Ok(()) } - pub fn remove_client(&mut self, client_id: ClientId) { + + pub fn remove_client(&mut self, client_id: ClientId) -> Result<()> { self.tabs.iter_mut().for_each(|(_, tab)| { tab.remove_client(client_id); if tab.has_no_connected_clients() { @@ -746,10 +794,11 @@ impl Screen { self.tab_history.remove(&client_id); } self.connected_clients.borrow_mut().remove(&client_id); - self.update_tabs(); + self.update_tabs() + .with_context(|| format!("failed to remote client {client_id:?}")) } - pub fn update_tabs(&self) { + pub fn update_tabs(&self) -> Result<()> { for (client_id, active_tab_index) in self.active_tab_indices.iter() { let mut tab_data = vec![]; for tab in self.tabs.values() { @@ -783,11 +832,17 @@ impl Screen { Some(*client_id), Event::TabUpdate(tab_data), )) - .unwrap(); + .or_else(|err| { + let (_, error_context) = err.0; + Err(anyhow!("failed to send data to plugins")) + .context(error_context) + .context("failed to update tabs") + })?; } + Ok(()) } - pub fn update_active_tab_name(&mut self, buf: Vec<u8>, client_id: ClientId) { + pub fn update_active_tab_name(&mut self, buf: Vec<u8>, client_id: ClientId) -> Result<()> { let s = str::from_utf8(&buf).unwrap(); if let Some(active_tab) = self.get_active_tab_mut(client_id) { match s { @@ -805,21 +860,28 @@ impl Screen { } }, } - self.update_tabs(); + self.update_tabs() + .context("failed to update active tabs name for client id: {client_id:?}") } else { - log::error!("Active tab not found for client id: {:?}", client_id); + log::error!("Active tab not found for client id: {client_id:?}"); + Ok(()) } } - pub fn undo_active_rename_tab(&mut self, client_id: ClientId) { + + pub fn undo_active_rename_tab(&mut self, client_id: ClientId) -> Result<()> { if let Some(active_tab) = self.get_active_tab_mut(client_id) { if active_tab.name != active_tab.prev_name { active_tab.name = active_tab.prev_name.clone(); - self.update_tabs(); + self.update_tabs() + .context("failed to undo renaming of active tab")?; } + Ok(()) } else { - log::error!("Active tab not found for client id: {:?}", client_id); + log::error!("Active tab not found for client id: {client_id:?}"); + Ok(()) } } + pub fn change_mode(&mut self, mode_info: ModeInfo, client_id: ClientId) { let previous_mode = self .mode_info @@ -866,33 +928,41 @@ impl Screen { tab.mark_active_pane_for_rerender(client_id); } } - pub fn move_focus_left_or_previous_tab(&mut self, client_id: ClientId) { + + pub fn move_focus_left_or_previous_tab(&mut self, client_id: ClientId) -> Result<()> { if let Some(active_tab) = self.get_active_tab_mut(client_id) { if !active_tab.move_focus_left(client_id) { - self.switch_tab_prev(client_id); + self.switch_tab_prev(client_id) + .context("failed to move focus left")?; } } else { log::error!("Active tab not found for client id: {:?}", client_id); } + Ok(()) } - pub fn move_focus_right_or_next_tab(&mut self, client_id: ClientId) { + pub fn move_focus_right_or_next_tab(&mut self, client_id: ClientId) -> Result<()> { if let Some(active_tab) = self.get_active_tab_mut(client_id) { if !active_tab.move_focus_right(client_id) { - self.switch_tab_next(client_id); + self.switch_tab_next(client_id) + .context("failed to move focus right")?; } } else { log::error!("Active tab not found for client id: {:?}", client_id); } + Ok(()) } - pub fn toggle_tab(&mut self, client_id: ClientId) { - let tab = self.get_previous_tab(client_id); + pub fn toggle_tab(&mut self, client_id: ClientId) -> Result<()> { + let tab = self + .get_previous_tab(client_id) + .context("failed to toggle tabs")?; if let Some(t) = tab { let position = t.position; - self.go_to_tab(position + 1, client_id); + self.go_to_tab(position + 1, client_id) + .context("failed to toggle tabs")?; }; - self.update_tabs(); - self.render(); + self.update_tabs().context("failed to toggle tabs")?; + self.render() } fn unblock_input(&self) { @@ -911,7 +981,7 @@ pub(crate) fn screen_thread_main( max_panes: Option<usize>, client_attributes: ClientAttributes, config_options: Box<Options>, -) { +) -> Result<()> { // let mut scrollbacks: HashMap<String, PaneId> = HashMap::new(); let capabilities = config_options.simplified_ui; let draw_pane_frames = config_options.pane_frames.unwrap_or(true); @@ -942,7 +1012,8 @@ pub(crate) fn screen_thread_main( let (event, mut err_ctx) = screen .bus .recv() - .expect("failed to receive event on channel"); + .context("failed to receive event on channel") + .fatal(); err_ctx.add_call(ContextType::Screen((&event).into())); match event { @@ -956,7 +1027,7 @@ pub(crate) fn screen_thread_main( } }, ScreenInstruction::Render => { - screen.render(); + screen.render()?; }, ScreenInstruction::NewPane(pid, client_or_tab_index) => { match client_or_tab_index { @@ -973,45 +1044,45 @@ pub(crate) fn screen_thread_main( }, }; screen.unblock_input(); - screen.update_tabs(); + screen.update_tabs()?; - screen.render(); + screen.render()?; }, ScreenInstruction::OpenInPlaceEditor(pid, client_id) => { active_tab!(screen, client_id, |tab: &mut Tab| tab .suppress_active_pane(pid, client_id)); screen.unblock_input(); - screen.update_tabs(); + screen.update_tabs()?; - screen.render(); + screen.render()?; }, ScreenInstruction::TogglePaneEmbedOrFloating(client_id) => { active_tab!(screen, client_id, |tab: &mut Tab| tab .toggle_pane_embed_or_floating(client_id)); screen.unblock_input(); - screen.update_tabs(); // update tabs so that the ui indication will be send to the plugins - screen.render(); + screen.update_tabs()?; // update tabs so that the ui indication will be send to the plugins + screen.render()?; }, ScreenInstruction::ToggleFloatingPanes(client_id, default_shell) => { active_tab!(screen, client_id, |tab: &mut Tab| tab .toggle_floating_panes(client_id, default_shell)); screen.unblock_input(); - screen.update_tabs(); // update tabs so that the ui indication will be send to the plugins - screen.render(); + screen.update_tabs()?; // update tabs so that the ui indication will be send to the plugins + screen.render()?; }, ScreenInstruction::HorizontalSplit(pid, client_id) => { active_tab!(screen, client_id, |tab: &mut Tab| tab .horizontal_split(pid, client_id)); screen.unblock_input(); - screen.update_tabs(); - screen.render(); + screen.update_tabs()?; + screen.render()?; }, ScreenInstruction::VerticalSplit(pid, client_id) => { active_tab!(screen, client_id, |tab: &mut Tab| tab .vertical_split(pid, client_id)); screen.unblock_input(); - screen.update_tabs(); - screen.render(); + screen.update_tabs()?; + screen.render()?; }, ScreenInstruction::WriteCharacter(bytes, client_id) => { active_tab!(screen, client_id, |tab: &mut Tab| { @@ -1024,166 +1095,166 @@ pub(crate) fn screen_thread_main( ScreenInstruction::ResizeLeft(client_id) => { active_tab!(screen, client_id, |tab: &mut Tab| tab .resize_left(client_id)); - screen.render(); + screen.render()?; }, ScreenInstruction::ResizeRight(client_id) => { active_tab!(screen, client_id, |tab: &mut Tab| tab .resize_right(client_id)); - screen.render(); + screen.render()?; }, ScreenInstruction::ResizeDown(client_id) => { active_tab!(screen, client_id, |tab: &mut Tab| tab .resize_down(client_id)); - screen.render(); + screen.render()?; }, ScreenInstruction::ResizeUp(client_id) => { active_tab!(screen, client_id, |tab: &mut Tab| tab.resize_up(client_id)); - screen.render(); + screen.render()?; }, ScreenInstruction::ResizeIncrease(client_id) => { active_tab!(screen, client_id, |tab: &mut Tab| tab .resize_increase(client_id)); - screen.render(); + screen.render()?; }, ScreenInstruction::ResizeDecrease(client_id) => { active_tab!(screen, client_id, |tab: &mut Tab| tab .resize_decrease(client_id)); - screen.render(); + screen.render()?; }, ScreenInstruction::SwitchFocus(client_id) => { active_tab!(screen, client_id, |tab: &mut Tab| tab .focus_next_pane(client_id)); - screen.render(); + screen.render()?; }, ScreenInstruction::FocusNextPane(client_id) => { active_tab!(screen, client_id, |tab: &mut Tab| tab .focus_next_pane(client_id)); - screen.render(); + screen.render()?; }, ScreenInstruction::FocusPreviousPane(client_id) => { active_tab!(screen, client_id, |tab: &mut Tab| tab .focus_previous_pane(client_id)); - screen.render(); + screen.render()?; }, ScreenInstruction::MoveFocusLeft(client_id) => { active_tab!(screen, client_id, |tab: &mut Tab| tab .move_focus_left(client_id)); - screen.render(); + screen.render()?; }, ScreenInstruction::MoveFocusLeftOrPreviousTab(client_id) => { - screen.move_focus_left_or_previous_tab(client_id); + screen.move_focus_left_or_previous_tab(client_id)?; screen.unblock_input(); - screen.render(); + screen.render()?; }, ScreenInstruction::MoveFocusDown(client_id) => { active_tab!(screen, client_id, |tab: &mut Tab| tab .move_focus_down(client_id)); - screen.render(); + screen.render()?; }, ScreenInstruction::MoveFocusRight(client_id) => { active_tab!(screen, client_id, |tab: &mut Tab| tab .move_focus_right(client_id)); - screen.render(); + screen.render()?; }, ScreenInstruction::MoveFocusRightOrNextTab(client_id) => { - screen.move_focus_right_or_next_tab(client_id); + screen.move_focus_right_or_next_tab(client_id)?; screen.unblock_input(); - screen.render(); + screen.render()?; }, ScreenInstruction::MoveFocusUp(client_id) => { active_tab!(screen, client_id, |tab: &mut Tab| tab .move_focus_up(client_id)); - screen.render(); + screen.render()?; }, ScreenInstruction::DumpScreen(file, client_id) => { active_tab!(screen, client_id, |tab: &mut Tab| tab .dump_active_terminal_screen(Some(file.to_string()), client_id)); - screen.render(); + screen.render()?; }, ScreenInstruction::EditScrollback(client_id) => { active_tab!(screen, client_id, |tab: &mut Tab| tab .edit_scrollback(client_id)); - screen.render(); + screen.render()?; }, ScreenInstruction::ScrollUp(client_id) => { active_tab!(screen, client_id, |tab: &mut Tab| tab .scroll_active_terminal_up(client_id)); - screen.render(); + screen.render()?; }, ScreenInstruction::MovePane(client_id) => { active_tab!(screen, client_id, |tab: &mut Tab| tab .move_active_pane(client_id)); - screen.render(); + screen.render()?; }, ScreenInstruction::MovePaneDown(client_id) => { active_tab!(screen, client_id, |tab: &mut Tab| tab .move_active_pane_down(client_id)); - screen.render(); + screen.render()?; }, ScreenInstruction::MovePaneUp(client_id) => { active_tab!(screen, client_id, |tab: &mut Tab| tab .move_active_pane_up(client_id)); - screen.render(); + screen.render()?; }, ScreenInstruction::MovePaneRight(client_id) => { active_tab!(screen, client_id, |tab: &mut Tab| tab .move_active_pane_right(client_id)); - screen.render(); + screen.render()?; }, ScreenInstruction::MovePaneLeft(client_id) => { active_tab!(screen, client_id, |tab: &mut Tab| tab .move_active_pane_left(client_id)); - screen.render(); + screen.render()?; }, ScreenInstruction::ScrollUpAt(point, client_id) => { active_tab!(screen, client_id, |tab: &mut Tab| tab .handle_scrollwheel_up(&point, 3, client_id)); - screen.render(); + screen.render()?; }, ScreenInstruction::ScrollDown(client_id) => { active_tab!(screen, client_id, |tab: &mut Tab| tab .scroll_active_terminal_down(client_id)); - screen.render(); + screen.render()?; }, ScreenInstruction::ScrollDownAt(point, client_id) => { active_tab!(screen, client_id, |tab: &mut Tab| tab .handle_scrollwheel_down(&point, 3, client_id)); - screen.render(); + screen.render()?; }, ScreenInstruction::ScrollToBottom(client_id) => { active_tab!(screen, client_id, |tab: &mut Tab| tab .scroll_active_terminal_to_bottom(client_id)); - screen.render(); + screen.render()?; }, ScreenInstruction::PageScrollUp(client_id) => { active_tab!(screen, client_id, |tab: &mut Tab| tab .scroll_active_terminal_up_page(client_id)); - screen.render(); + screen.render()?; }, ScreenInstruction::PageScrollDown(client_id) => { active_tab!(screen, client_id, |tab: &mut Tab| tab .scroll_active_terminal_down_page(client_id)); - screen.render(); + screen.render()?; }, ScreenInstruction::HalfPageScrollUp(client_id) => { active_tab!(screen, client_id, |tab: &mut Tab| tab .scroll_active_terminal_up_half_page(client_id)); - screen.render(); + |