rmux-server 0.10.0

Tokio daemon and request dispatcher for the RMUX terminal multiplexer.
Documentation
use std::sync::Arc;

use rmux_proto::{RmuxError, Target};

use super::super::attach_support::{ActiveAttach, ActiveAttachIdentity};
use super::super::lifecycle_support::LifecycleTargetLease;
use super::super::scripting_support::QueueExecutionContext;
use super::super::{RequestHandler, StableTargetIdentity};
use super::state::ClientOverlayState;
use crate::pane_io::{AttachControl, OverlayFrame};
use crate::pane_terminals::HandlerState;

#[derive(Debug, Clone, PartialEq, Eq)]
pub(in crate::handler) struct OverlayIdentity {
    client: ActiveAttachIdentity,
    target: StableTargetIdentity,
    target_lease: Option<Arc<LifecycleTargetLease>>,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) enum OverlayActionStatus {
    Current,
    Retired,
    Missing,
}

impl OverlayActionStatus {
    pub(super) fn is_current(self) -> bool {
        self == Self::Current
    }

    pub(super) fn was_retired(self) -> bool {
        self == Self::Retired
    }
}

impl OverlayIdentity {
    pub(super) fn capture(
        state: &mut HandlerState,
        client: ActiveAttachIdentity,
        target: Target,
    ) -> Result<Self, RmuxError> {
        let target_lease = match &target {
            Target::Pane(target) => state.capture_retained_pane_lifecycle_target(target),
            Target::Window(target) => state.capture_retained_window_lifecycle_target(target),
            Target::Session(_) => None,
        };
        Ok(Self {
            client,
            target: StableTargetIdentity::capture(state, target)?,
            target_lease,
        })
    }

    pub(super) fn command_context(
        &self,
        context: QueueExecutionContext,
        target: Target,
    ) -> QueueExecutionContext {
        context
            .with_current_target(Some(target))
            .with_retained_lifecycle_target(self.target_lease.clone())
            .with_pinned_current_target_identity(Some(self.target.clone()))
    }

    pub(super) fn matches(
        &self,
        state: &HandlerState,
        active: &ActiveAttach,
        target: &Target,
    ) -> bool {
        self.client.matches_active(active)
            && active.session_id == self.client.session_id()
            && !active.suspended
            && !active.closing.load(std::sync::atomic::Ordering::SeqCst)
            && state
                .sessions
                .session(&active.session_name)
                .is_some_and(|session| session.id() == self.client.session_id())
            && self.target.matches_target(state, target)
    }

    pub(super) fn rename_session(
        &mut self,
        old_name: &rmux_proto::SessionName,
        new_name: &rmux_proto::SessionName,
    ) {
        self.target.rename_session(old_name, new_name);
    }
}

impl RequestHandler {
    pub(super) async fn overlay_action_is_current(
        &self,
        attach_pid: u32,
        input_identity: Option<ActiveAttachIdentity>,
    ) -> Result<OverlayActionStatus, RmuxError> {
        let retired = {
            let state = self.state.lock().await;
            let mut active_attach = self.active_attach.lock().await;
            let Some(active) = active_attach.by_pid.get_mut(&attach_pid) else {
                return Ok(OverlayActionStatus::Missing);
            };
            if input_identity.is_some_and(|identity| !identity.matches_active(active)) {
                return Ok(OverlayActionStatus::Missing);
            }
            let Some(overlay) = active.overlay.as_ref() else {
                return Ok(OverlayActionStatus::Missing);
            };
            if overlay
                .identity()
                .matches(&state, active, overlay.current_target())
            {
                return Ok(OverlayActionStatus::Current);
            }

            let popup_job = match active.overlay.take() {
                Some(ClientOverlayState::Popup(popup)) => popup.job,
                Some(ClientOverlayState::Menu(_)) | None => None,
            };
            active.overlay_generation = active.overlay_generation.saturating_add(1);
            let transient_restore = active
                .transient_message
                .is_some()
                .then(|| (active.identity(attach_pid), active.session_name.clone()));
            Some((
                active.control_tx.clone(),
                active.render_generation,
                active.overlay_generation,
                popup_job,
                transient_restore,
            ))
        };

        if let Some((
            control_tx,
            render_generation,
            overlay_generation,
            popup_job,
            transient_restore,
        )) = retired
        {
            if let Some(job) = popup_job {
                job.terminate();
            }
            let _ = control_tx.send(AttachControl::Overlay(OverlayFrame::persistent(
                Vec::new(),
                render_generation,
                overlay_generation,
            )));
            self.restore_transient_message_after_persistent_clear(transient_restore)
                .await;
        }
        Ok(OverlayActionStatus::Retired)
    }
}