Merge remote-tracking branch 'origin/main' into blitz/command-bar
This commit is contained in:
@@ -7,6 +7,7 @@
|
||||
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::path::PathBuf;
|
||||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
@@ -23,6 +24,7 @@ use crate::pty::{pipe_pane_command, PtyBridge};
|
||||
use crate::sidecar::{SidecarError, SidecarStore};
|
||||
use crate::spawn_history::{RecentDir, SpawnHistory, SpawnHistoryRecord};
|
||||
use crate::tmux::TmuxError;
|
||||
use crate::viewport::{ViewportId, Viewports};
|
||||
use crate::worktree::{WorktreeError, WorktreeManager};
|
||||
|
||||
/// Capacity of the session-events broadcast channel. A subscriber that falls
|
||||
@@ -166,6 +168,14 @@ struct Inner {
|
||||
report_ttl: Duration,
|
||||
sessions: Mutex<HashMap<Uuid, SessionRecord>>,
|
||||
events: broadcast::Sender<CatalogEvent>,
|
||||
/// Sizes reported by the clients attached to each session, and what tmux
|
||||
/// was last told. See [`crate::viewport`] for why the window is not simply
|
||||
/// set to whatever arrived most recently.
|
||||
viewports: Mutex<Viewports>,
|
||||
/// Hands out [`ViewportId`]s. Monotonic for the daemon's lifetime, so a
|
||||
/// client that reconnects never collides with the registration its
|
||||
/// previous socket has not finished releasing.
|
||||
next_viewport: AtomicU64,
|
||||
}
|
||||
|
||||
impl SessionManager {
|
||||
@@ -206,6 +216,8 @@ impl SessionManager {
|
||||
report_ttl: crate::activity::REPORT_TTL,
|
||||
sessions: Mutex::new(HashMap::new()),
|
||||
events,
|
||||
viewports: Mutex::new(Viewports::new()),
|
||||
next_viewport: AtomicU64::new(0),
|
||||
}),
|
||||
}
|
||||
}
|
||||
@@ -770,6 +782,52 @@ impl SessionManager {
|
||||
.map_err(SessionError::Tmux)
|
||||
}
|
||||
|
||||
/// Register a client that is about to start reporting its display size,
|
||||
/// and hand back the handle it reports under.
|
||||
///
|
||||
/// Cheap and infallible: the session it will report against is not
|
||||
/// consulted until the first [`Self::set_viewport`].
|
||||
pub fn attach_viewport(&self) -> ViewportId {
|
||||
self.inner.next_viewport.fetch_add(1, Ordering::Relaxed)
|
||||
}
|
||||
|
||||
/// A client reported the size it can display.
|
||||
///
|
||||
/// The shared tmux window is resized only when the size arbitrated across
|
||||
/// every attached client actually changes — see [`crate::viewport`]. Two
|
||||
/// clients of different sizes used to resize the window past each other on
|
||||
/// every report, which leaves a full-screen agent repainting into rows it
|
||||
/// never clears and looking, to the operator, like a pane that has stopped
|
||||
/// accepting input.
|
||||
pub async fn set_viewport(
|
||||
&self,
|
||||
id: Uuid,
|
||||
viewport: ViewportId,
|
||||
cols: u16,
|
||||
rows: u16,
|
||||
) -> Result<(), SessionError> {
|
||||
let target = self
|
||||
.inner
|
||||
.viewports
|
||||
.lock()
|
||||
.await
|
||||
.set(id, viewport, cols, rows);
|
||||
match target {
|
||||
Some((cols, rows)) => self.resize(id, cols, rows).await,
|
||||
None => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Drop a detached client's claim on the window size, resizing to what the
|
||||
/// clients still attached can display. Errors are the caller's to ignore:
|
||||
/// a socket closing because its session died has nothing left to resize.
|
||||
pub async fn release_viewport(&self, id: Uuid, viewport: ViewportId) {
|
||||
let target = self.inner.viewports.lock().await.release(id, viewport);
|
||||
if let Some((cols, rows)) = target {
|
||||
let _ = self.resize(id, cols, rows).await;
|
||||
}
|
||||
}
|
||||
|
||||
/// Rename a session (#125): set `metadata.name`, or clear it when the
|
||||
/// requested name is blank so the session falls back to its command.
|
||||
///
|
||||
|
||||
Reference in New Issue
Block a user