rmux-server 0.10.0

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

use rmux_core::{PaneGeometry, PaneId};
use rmux_proto::PaneTarget;

use crate::input_keys::MouseForwardEvent;
#[cfg(test)]
pub(crate) use crate::pane_scrollbar::PaneScrollbarsMode;
pub(crate) use crate::pane_scrollbar::{PaneScrollbar, ScrollbarPosition};
pub(crate) use crate::status_ranges::{StatusLineLayout, StatusRange, StatusRangeType};

#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub(crate) enum MouseLocation {
    #[default]
    Nowhere,
    Pane,
    Status,
    StatusLeft,
    StatusRight,
    StatusDefault,
    ScrollbarUp,
    ScrollbarSlider,
    ScrollbarDown,
    Border,
    Control(u8),
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum MouseEventKind {
    MouseMove,
    MouseDown,
    MouseUp,
    MouseDrag,
    MouseDragEnd,
    WheelDown,
    WheelUp,
    SecondClick,
    DoubleClick,
    TripleClick,
}

#[allow(dead_code)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum PaneBorderStatus {
    Off,
    Top,
    Bottom,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct BorderControlRange {
    pub(crate) x: std::ops::RangeInclusive<u16>,
    pub(crate) y: u16,
    pub(crate) control: u8,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct PaneMouseTarget {
    pub(crate) pane_id: PaneId,
    pub(crate) pane_target: Option<PaneTarget>,
    pub(crate) window_id: u32,
    pub(crate) geometry: PaneGeometry,
    pub(crate) scrollbar: Option<PaneScrollbar>,
    pub(crate) border_controls: Vec<BorderControlRange>,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct MouseLayout {
    pub(crate) session_id: u32,
    pub(crate) status_at: Option<u16>,
    pub(crate) status_lines: u16,
    pub(crate) status: Option<StatusLineLayout>,
    pub(crate) pane_border_status: PaneBorderStatus,
    pub(crate) focus_follows_mouse: bool,
    pub(crate) active_pane: Option<PaneId>,
    pub(crate) panes: Vec<PaneMouseTarget>,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) enum MouseDragHandler {
    CopyModeSelection { target: PaneTarget },
    CopyModeScrollbar { target: PaneTarget },
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct AttachedMouseEvent {
    pub(crate) raw: MouseForwardEvent,
    pub(crate) session_id: u32,
    pub(crate) window_id: Option<u32>,
    pub(crate) pane_id: Option<PaneId>,
    pub(crate) pane_target: Option<PaneTarget>,
    pub(crate) location: MouseLocation,
    pub(crate) status_at: Option<u16>,
    pub(crate) status_lines: u16,
    pub(crate) ignore: bool,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct ClassifiedMouseEvent {
    pub(crate) key: rmux_core::KeyCode,
    pub(crate) event: AttachedMouseEvent,
    pub(crate) focus_target: Option<PaneId>,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct PanePassthroughMouseEvent {
    pub(crate) event: AttachedMouseEvent,
    pub(crate) focus_target: Option<PaneId>,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) struct MouseClickTimerToken {
    deadline: Instant,
    generation: u64,
}

impl MouseClickTimerToken {
    pub(crate) const fn deadline(self) -> Instant {
        self.deadline
    }
}

#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub(crate) struct ClientMouseState {
    pub(crate) click_deadline: Option<Instant>,
    pub(crate) click_timer_generation: u64,
    pub(crate) double_click_pending: bool,
    pub(crate) triple_click_pending: bool,
    pub(crate) click_button: u16,
    pub(crate) click_location: MouseLocation,
    pub(crate) click_pane: Option<PaneId>,
    pub(crate) click_event: Option<AttachedMouseEvent>,
    pub(crate) drag_flag: u8,
    pub(crate) scrolling_flag: bool,
    pub(crate) slider_mpos: i32,
    pub(crate) current_event: Option<AttachedMouseEvent>,
    pub(crate) drag_start_event: Option<AttachedMouseEvent>,
    pub(crate) drag_handler: Option<MouseDragHandler>,
}

impl ClientMouseState {
    pub(crate) const fn click_deadline(&self) -> Option<Instant> {
        self.click_deadline
    }

    pub(crate) fn click_timer_token(&self) -> Option<MouseClickTimerToken> {
        self.click_deadline.map(|deadline| MouseClickTimerToken {
            deadline,
            generation: self.click_timer_generation,
        })
    }

    pub(crate) fn click_timer_matches(&self, token: MouseClickTimerToken) -> bool {
        self.click_timer_token() == Some(token)
    }

    pub(crate) fn arm_click_timer(&mut self, deadline: Instant) {
        self.click_timer_generation = self.click_timer_generation.saturating_add(1);
        self.click_deadline = Some(deadline);
    }

    pub(crate) fn clear_click_timer_if_current(&mut self, token: MouseClickTimerToken) -> bool {
        if !self.click_timer_matches(token) {
            return false;
        }
        self.clear_click_timer_state();
        true
    }

    pub(crate) fn reset_for_session_switch(&mut self) {
        let next_generation = self.click_timer_generation.saturating_add(1);
        *self = Self {
            click_timer_generation: next_generation,
            slider_mpos: -1,
            ..Self::default()
        };
    }

    pub(crate) fn clear_click_timer_state(&mut self) {
        self.click_deadline = None;
        self.double_click_pending = false;
        self.triple_click_pending = false;
        self.click_event = None;
    }
}