rmux-server 0.1.1

Tokio daemon and request dispatcher for the RMUX terminal multiplexer.
Documentation
use rmux_proto::{PaneTarget, RmuxError, Target};

use super::super::RequestHandler;
use super::support::{find_session_name_by_id, find_window_target_by_id};
use crate::mouse::{AttachedMouseEvent, MouseLocation};

impl RequestHandler {
    pub(in crate::handler) async fn attached_mouse_target(
        &self,
        attach_pid: u32,
        event: &AttachedMouseEvent,
    ) -> Result<Option<Target>, RmuxError> {
        let session_name = self.attached_session_name(attach_pid).await?;
        self.overlay_target_from_mouse(session_name, Some(event))
            .await
    }

    pub(super) async fn resolve_overlay_client(
        &self,
        requester_pid: u32,
        target_client: Option<&str>,
        command_name: &str,
    ) -> Result<u32, RmuxError> {
        if let Some(target_client) = target_client {
            if target_client == "=" {
                return self
                    .resolve_attached_client_pid(requester_pid, command_name)
                    .await;
            }
            let pid = target_client.parse::<u32>().map_err(|_| {
                RmuxError::Server(format!("invalid {command_name} client '{target_client}'"))
            })?;
            let active_attach = self.active_attach.lock().await;
            if active_attach.by_pid.contains_key(&pid) {
                Ok(pid)
            } else {
                Err(RmuxError::Server(format!(
                    "{command_name} client {pid} is not attached"
                )))
            }
        } else {
            self.resolve_attached_client_pid(requester_pid, command_name)
                .await
        }
    }

    pub(super) async fn resolve_overlay_target(
        &self,
        attach_pid: u32,
        explicit_pane: Option<PaneTarget>,
        current_target: Option<Target>,
    ) -> Result<Target, RmuxError> {
        if let Some(target) = explicit_pane {
            return Ok(Target::Pane(target));
        }
        if let Some(target) = current_target {
            return Ok(target);
        }

        let session_name = self.attached_session_name(attach_pid).await?;
        let mouse_event = {
            let active_attach = self.active_attach.lock().await;
            active_attach
                .by_pid
                .get(&attach_pid)
                .and_then(|active| active.mouse.current_event.clone())
        };
        if let Some(target) = self
            .overlay_target_from_mouse(session_name.clone(), mouse_event.as_ref())
            .await?
        {
            return Ok(target);
        }
        Ok(Target::Pane(self.attached_input_target(attach_pid).await?))
    }

    async fn overlay_target_from_mouse(
        &self,
        attached_session: rmux_proto::SessionName,
        event: Option<&AttachedMouseEvent>,
    ) -> Result<Option<Target>, RmuxError> {
        let Some(event) = event else {
            return Ok(None);
        };
        if let Some(target) = event.pane_target.clone() {
            return Ok(Some(Target::Pane(target)));
        }
        let state = self.state.lock().await;
        match event.location {
            MouseLocation::StatusLeft => Ok(Some(Target::Session(attached_session))),
            MouseLocation::Status | MouseLocation::StatusDefault | MouseLocation::StatusRight => {
                if let Some(window_id) = event.window_id {
                    if let Some(target) =
                        find_window_target_by_id(&state, &attached_session, window_id)
                    {
                        return Ok(Some(Target::Window(target)));
                    }
                }
                if let Some(session_name) = find_session_name_by_id(&state, event.session_id) {
                    return Ok(Some(Target::Session(session_name)));
                }
                Ok(None)
            }
            _ => Ok(None),
        }
    }
}