par-term 0.30.4

Cross-platform GPU-accelerated terminal emulator with inline graphics support (Sixel, iTerm2, Kitty)
//! Flow control and miscellaneous tmux notification handlers.
//!
//! Covers pane focus changes, error reporting, pause/continue flow control,
//! and the (currently dead-code) sync-action dispatcher.

use crate::app::window_state::WindowState;
use crate::tmux::SyncAction;

impl WindowState {
    /// Handle pane focus changed notification from external tmux
    pub(super) fn handle_tmux_pane_focus_changed(&mut self, tmux_pane_id: crate::tmux::TmuxPaneId) {
        crate::debug_info!("TMUX", "Pane focus changed to %{}", tmux_pane_id);

        // Update the tmux session's focused pane
        if let Some(session) = &mut self.tmux_state.tmux_session {
            session.set_focused_pane(Some(tmux_pane_id));
        }

        // Update the native pane focus to match
        if let Some(native_pane_id) = self.tmux_state.tmux_pane_to_native_pane.get(&tmux_pane_id) {
            // Find the tab containing this pane and update its focus
            if let Some(tab) = self.tab_manager.active_tab_mut()
                && let Some(pm) = tab.pane_manager_mut()
            {
                pm.focus_pane(*native_pane_id);
                crate::debug_info!(
                    "TMUX",
                    "Updated native pane focus: tmux %{} -> native {}",
                    tmux_pane_id,
                    native_pane_id
                );
            }
        }
    }

    /// Handle error notification
    pub(super) fn handle_tmux_error(&mut self, msg: &str) {
        crate::debug_error!("TMUX", "Error from tmux: {}", msg);

        // Show notification to user
        self.deliver_notification("tmux Error", msg);
    }

    /// Handle pause notification (for slow connections)
    pub(super) fn handle_tmux_pause(&mut self) {
        crate::debug_info!("TMUX", "Received pause notification - buffering output");

        // Set paused state in sync manager
        self.tmux_state.tmux_sync.pause();

        // Show toast notification to user
        self.show_toast("tmux: Output paused (slow connection)");
    }

    /// Handle continue notification (resume after pause)
    pub(super) fn handle_tmux_continue(&mut self) {
        crate::debug_info!("TMUX", "Received continue notification - resuming output");

        // Get and flush buffered output
        let buffered = self.tmux_state.tmux_sync.resume();

        // Flush buffered data to each pane
        for (tmux_pane_id, data) in buffered {
            if !data.is_empty() {
                crate::debug_info!(
                    "TMUX",
                    "Flushing {} buffered bytes to pane %{}",
                    data.len(),
                    tmux_pane_id
                );

                // Find the native pane and send the buffered data
                if let Some(native_pane_id) =
                    self.tmux_state.tmux_sync.get_native_pane(tmux_pane_id)
                {
                    // Find the pane across all tabs
                    for tab in self.tab_manager.tabs_mut() {
                        if let Some(pane_manager) = tab.pane_manager_mut()
                            && let Some(pane) = pane_manager.get_pane_mut(native_pane_id)
                        {
                            // try_lock: intentional — flushing buffered output in the sync
                            // event loop. On miss: this buffered chunk is lost. Low risk:
                            // the tmux %continue state means output has resumed and fresh
                            // data will arrive shortly to fill in any gap.
                            if let Ok(term) = pane.terminal.try_write() {
                                term.process_data(&data);
                            }
                            break;
                        }
                    }
                }
            }
        }

        // Show toast notification to user
        self.show_toast("tmux: Output resumed");
    }

    /// Process sync actions generated by `TmuxSync::process_notifications`.
    ///
    /// Called from `check_tmux_notifications` (polling.rs) once per group of notifications
    /// (session → layout → output → other) to drive UI state mutations with pre-translated
    /// native IDs.
    ///
    /// Returns `true` if any action requires a redraw.
    pub(super) fn process_sync_actions(&mut self, actions: Vec<SyncAction>) -> bool {
        let mut needs_redraw = false;
        for action in actions {
            match action {
                SyncAction::CreateTab { window_id } => {
                    crate::debug_info!("TMUX", "Sync: Create tab for window @{}", window_id);
                    self.handle_tmux_window_add(window_id);
                    needs_redraw = true;
                }
                SyncAction::CloseTab { tab_id } => {
                    crate::debug_info!("TMUX", "Sync: Close tab {}", tab_id);
                    // Note: TmuxSync already called unmap_window when producing this action,
                    // so we only need to close the tab and handle the last-tab case.
                    let was_last = self.tab_manager.close_tab(tab_id);
                    if was_last {
                        self.handle_tmux_session_ended();
                    }
                    needs_redraw = true;
                }
                SyncAction::RenameTab { tab_id, name } => {
                    crate::debug_info!("TMUX", "Sync: Rename tab {} to '{}'", tab_id, name);
                    if let Some(tab) = self.tab_manager.get_tab_mut(tab_id) {
                        tab.set_title(&name);
                        needs_redraw = true;
                    }
                }
                SyncAction::UpdateLayout { tab_id, layout } => {
                    crate::debug_info!("TMUX", "Sync: Update layout for tab {}", tab_id);
                    // Reverse-lookup the tmux window ID so handle_tmux_layout_change can use
                    // it for mapping and pane-tree reconciliation.
                    if let Some(window_id) = self.tmux_state.tmux_sync.get_window(tab_id) {
                        self.handle_tmux_layout_change(window_id, &layout);
                        needs_redraw = true;
                    }
                }
                SyncAction::PaneOutput { pane_id, data } => {
                    crate::debug_trace!(
                        "TMUX",
                        "Sync: Route {} bytes to pane {}",
                        data.len(),
                        pane_id
                    );
                    // pane_id is already a native PaneId (translated by TmuxSync).
                    // Route directly to the pane's terminal across all tabs.
                    // try_lock: intentional — called from the sync event loop. On miss: this
                    // chunk of pane output is dropped. Acceptable; tmux backpressure via
                    // %pause/%continue handles flow control and recovery.
                    for tab in self.tab_manager.tabs_mut() {
                        if let Some(pane_manager) = tab.pane_manager_mut()
                            && let Some(pane) = pane_manager.get_pane_mut(pane_id)
                            && let Ok(term) = pane.terminal.try_write()
                        {
                            term.process_data(&data);
                            break;
                        }
                    }
                }
                SyncAction::SessionEnded => {
                    crate::debug_info!("TMUX", "Sync: Session ended");
                    self.handle_tmux_session_ended();
                    needs_redraw = true;
                }
                SyncAction::Pause => {
                    crate::debug_info!("TMUX", "Sync: Pause");
                    self.handle_tmux_pause();
                }
                SyncAction::Continue => {
                    crate::debug_info!("TMUX", "Sync: Continue");
                    self.handle_tmux_continue();
                    needs_redraw = true;
                }
            }
        }
        needs_redraw
    }
}