rmux-server 0.10.0

Tokio daemon and request dispatcher for the RMUX terminal multiplexer.
Documentation
use super::key_binding::DirectCopyModeCommand;
use super::{ensure_copy_mode_session_identity, ActiveAttachIdentity, RequestHandler};
use crate::copy_mode::{CopyModeLineNumberLayout, CopyModeMouseContext};
use crate::mouse::{
    copy_mode_mouse_context_with_line_numbers,
    copy_mode_mouse_drag_start_context_with_line_numbers, AttachedMouseEvent,
};
use rmux_proto::{OptionName, PaneTarget};

#[derive(Debug, Clone)]
pub(super) enum CopyModeCommandOrigin {
    NonMouse,
    Mouse(AttachedMouseEvent),
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) enum CopyModeMouseContextKind {
    Position,
    DragSelectionStart,
}

impl From<Option<AttachedMouseEvent>> for CopyModeCommandOrigin {
    fn from(event: Option<AttachedMouseEvent>) -> Self {
        match event {
            Some(event) => Self::Mouse(event),
            None => Self::NonMouse,
        }
    }
}

pub(super) struct CopyModeCommandInvocation<'a> {
    pub(super) target: PaneTarget,
    pub(super) command: &'a str,
    pub(super) args: &'a [String],
    pub(super) repeat_count: usize,
    pub(super) origin: CopyModeCommandOrigin,
}

impl RequestHandler {
    pub(in crate::handler) async fn execute_direct_copy_mode_binding(
        &self,
        requester_pid: u32,
        identity: Option<ActiveAttachIdentity>,
        target: PaneTarget,
        command: &DirectCopyModeCommand,
        mouse_event: Option<AttachedMouseEvent>,
    ) -> Result<(), rmux_proto::RmuxError> {
        match (identity, mouse_event) {
            (Some(identity), Some(mouse_event)) => {
                self.execute_copy_mode_command_for_identity_with_mouse_event(
                    identity,
                    target,
                    &command.command,
                    &command.args,
                    command.repeat_count,
                    mouse_event,
                )
                .await
            }
            (Some(identity), None) => {
                self.execute_copy_mode_command_for_identity(
                    identity,
                    target,
                    &command.command,
                    &command.args,
                    command.repeat_count,
                )
                .await
            }
            (None, Some(mouse_event)) => {
                self.execute_copy_mode_command_with_mouse_event(
                    requester_pid,
                    target,
                    &command.command,
                    &command.args,
                    command.repeat_count,
                    mouse_event,
                )
                .await
            }
            (None, None) => {
                self.execute_copy_mode_command(
                    requester_pid,
                    target,
                    &command.command,
                    &command.args,
                    command.repeat_count,
                )
                .await
            }
        }
    }
}

pub(super) async fn mouse_context_for_origin(
    handler: &RequestHandler,
    requester_pid: u32,
    identity: Option<ActiveAttachIdentity>,
    target: &PaneTarget,
    event: &AttachedMouseEvent,
    kind: CopyModeMouseContextKind,
) -> Option<CopyModeMouseContext> {
    let attach_pid = match identity {
        Some(identity) => identity.attach_pid(),
        None => handler
            .resolve_attached_client_pid(requester_pid, "send-keys")
            .await
            .ok()?,
    };
    let (slider_mpos, expected_session_id) = {
        let active_attach = handler.active_attach.lock().await;
        let active = active_attach.by_pid.get(&attach_pid).filter(|active| {
            identity.is_none_or(|identity| {
                identity.matches_active(active)
                    && !active.closing.load(std::sync::atomic::Ordering::SeqCst)
            })
        })?;
        (
            active.mouse.slider_mpos,
            identity.map(|_| active.session_id),
        )
    };
    let state = handler.state.lock().await;
    ensure_copy_mode_session_identity(&state, target, expected_session_id).ok()?;
    let session = state.sessions.session(target.session_name())?;
    let pane = session
        .window_at(target.window_index())?
        .pane(target.pane_index())?;
    let summary = state.pane_copy_mode_summary(target.session_name(), pane.id())?;
    let line_numbers = CopyModeLineNumberLayout::resolve(
        state.options.resolve_for_pane(
            target.session_name(),
            target.window_index(),
            target.pane_index(),
            OptionName::CopyModeLineNumbers,
        ),
        summary.line_numbers_enabled,
        summary.history_size,
        summary.backing_rows,
        summary.scroll_position,
        summary.cursor_y,
    );
    let pane_geometry = crate::mouse::layout_for_session(&state, target.session_name(), 1)
        .and_then(|layout| {
            layout
                .panes
                .into_iter()
                .find(|candidate| candidate.pane_id == pane.id())
                .map(|candidate| candidate.geometry)
        })
        .unwrap_or_else(|| pane.geometry());
    let mut context = match kind {
        CopyModeMouseContextKind::Position => copy_mode_mouse_context_with_line_numbers(
            event,
            pane_geometry,
            slider_mpos,
            line_numbers,
        ),
        CopyModeMouseContextKind::DragSelectionStart => {
            copy_mode_mouse_drag_start_context_with_line_numbers(
                event,
                pane_geometry,
                slider_mpos,
                line_numbers,
            )
        }
    }?;
    context.move_cursor_before_command = !event.is_wheel();
    Some(context)
}