rmux-server 0.1.1

Tokio daemon and request dispatcher for the RMUX terminal multiplexer.
Documentation
use std::sync::{Arc, Mutex as StdMutex};

use rmux_core::{BoxLines, Style};
use rmux_proto::{Target, TerminalSize};

use crate::renderer::{render_popup_overlay, OverlayRect, PopupRenderSpec};

use super::menu::MenuOverlayState;
use super::popup_job::{PopupDragMode, PopupJob, PopupSurface};

#[derive(Debug, Clone)]
pub(in crate::handler) enum ClientOverlayState {
    Menu(Box<MenuOverlayState>),
    Popup(Box<PopupOverlayState>),
}

impl ClientOverlayState {
    pub(super) fn id(&self) -> u64 {
        match self {
            Self::Menu(menu) => menu.id,
            Self::Popup(popup) => popup.id,
        }
    }

    pub(super) fn render(&self) -> Vec<u8> {
        match self {
            Self::Menu(menu) => menu.render(),
            Self::Popup(popup) => popup.render(),
        }
    }
}

#[derive(Debug, Clone)]
pub(in crate::handler) struct PopupOverlayState {
    pub(in crate::handler) id: u64,
    pub(in crate::handler) requester_pid: u32,
    pub(in crate::handler) current_target: Target,
    pub(in crate::handler) rect: OverlayRect,
    pub(in crate::handler) preferred_width: u16,
    pub(in crate::handler) preferred_height: u16,
    pub(in crate::handler) title: String,
    pub(in crate::handler) style: Style,
    pub(in crate::handler) border_style: Style,
    pub(in crate::handler) border_lines: BoxLines,
    pub(in crate::handler) close_on_exit: bool,
    pub(in crate::handler) close_on_zero_exit: bool,
    pub(in crate::handler) close_any_key: bool,
    pub(in crate::handler) no_job: bool,
    pub(in crate::handler) surface: Arc<StdMutex<PopupSurface>>,
    pub(in crate::handler) job: Option<PopupJob>,
    pub(in crate::handler) nested_menu: Option<MenuOverlayState>,
    pub(in crate::handler) dragging: PopupDragMode,
}

impl PopupOverlayState {
    fn render(&self) -> Vec<u8> {
        let popup_frame = render_popup_overlay(&PopupRenderSpec {
            rect: self.rect,
            title: self.title.clone(),
            style: self.style.clone(),
            border_style: self.border_style.clone(),
            border_lines: self.border_lines,
            content_lines: self.surface.lock().expect("popup surface").lines(),
        });
        if let Some(menu) = &self.nested_menu {
            let mut frame = popup_frame;
            frame.extend_from_slice(&menu.render());
            frame
        } else {
            popup_frame
        }
    }

    pub(super) fn content_origin(&self) -> (u16, u16) {
        if self.border_lines.visible() {
            (self.rect.x.saturating_add(1), self.rect.y.saturating_add(1))
        } else {
            (self.rect.x, self.rect.y)
        }
    }

    pub(super) fn content_size(&self) -> TerminalSize {
        let rect = if self.border_lines.visible() {
            OverlayRect {
                x: self.rect.x.saturating_add(1),
                y: self.rect.y.saturating_add(1),
                width: self.rect.width.saturating_sub(2),
                height: self.rect.height.saturating_sub(2),
            }
        } else {
            self.rect
        };
        TerminalSize {
            cols: rect.width,
            rows: rect.height,
        }
    }
}