aboutsummaryrefslogtreecommitdiff
path: root/src/render_signal.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/render_signal.rs')
-rw-r--r--src/render_signal.rs104
1 files changed, 66 insertions, 38 deletions
diff --git a/src/render_signal.rs b/src/render_signal.rs
index f4b4391d..a6ebd839 100644
--- a/src/render_signal.rs
+++ b/src/render_signal.rs
@@ -16,7 +16,13 @@ pub(crate) struct RenderRequest {
#[derive(Debug, Default)]
pub(crate) struct RenderSignal {
pending: AtomicBool,
- request: Mutex<RenderRequest>,
+ state: Mutex<RenderSignalState>,
+}
+
+#[derive(Debug, Default)]
+struct RenderSignalState {
+ request: RenderRequest,
+ immediate_pty_sources: HashSet<PaneId>,
}
impl RenderSignal {
@@ -29,78 +35,75 @@ impl RenderSignal {
}
pub(crate) fn request_generic(&self) {
- let mut request = self
- .request
+ let mut state = self
+ .state
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
- request.generic = true;
+ state.request.generic = true;
self.pending.store(true, Ordering::Release);
}
- /// Returns true when the signal becomes pending or a new PTY source joins it.
- ///
- /// A new source may be visible even when the existing pending sources are
- /// hidden, so the consumer must re-evaluate the coalesced request.
+ /// Returns true when the signal becomes pending or visible PTY work joins it.
pub(crate) fn request_pty(&self, pane_id: PaneId) -> bool {
- let mut request = self
- .request
+ let mut state = self
+ .state
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
- let source_added = request.pty_sources.insert(pane_id);
+ let source_added = state.request.pty_sources.insert(pane_id);
+ let wake_for_source = source_added && state.immediate_pty_sources.contains(&pane_id);
let became_pending = !self.pending.swap(true, Ordering::AcqRel);
- became_pending || source_added
+ became_pending || wake_for_source
}
- pub(crate) fn has_generic_or_terminal_title(&self) -> bool {
- let request = self
- .request
+ pub(crate) fn set_immediate_pty_sources(&self, sources: HashSet<PaneId>) {
+ self.state
.lock()
- .unwrap_or_else(std::sync::PoisonError::into_inner);
- request.generic || !request.terminal_title_sources.is_empty()
+ .unwrap_or_else(std::sync::PoisonError::into_inner)
+ .immediate_pty_sources = sources;
}
- /// Checks pending PTY origins without allocating a source snapshot.
- /// Keep the predicate narrow because producers share this lock.
- pub(crate) fn has_pty_source_matching(
- &self,
- mut predicate: impl FnMut(PaneId) -> bool,
- ) -> bool {
- self.request
+ pub(crate) fn has_immediate_work(&self) -> bool {
+ let state = self
+ .state
.lock()
- .unwrap_or_else(std::sync::PoisonError::into_inner)
- .pty_sources
- .iter()
- .copied()
- .any(&mut predicate)
+ .unwrap_or_else(std::sync::PoisonError::into_inner);
+ state.request.generic
+ || !state.request.terminal_title_sources.is_empty()
+ || state
+ .request
+ .pty_sources
+ .iter()
+ .any(|pane_id| state.immediate_pty_sources.contains(pane_id))
}
/// Coalesces terminal-title changes separately from ordinary PTY damage so
/// consumers can update metadata without inspecting every pane.
pub(crate) fn request_terminal_title(&self, pane_id: PaneId) -> bool {
- let mut request = self
- .request
+ let mut state = self
+ .state
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
- let source_added = request.terminal_title_sources.insert(pane_id);
+ let source_added = state.request.terminal_title_sources.insert(pane_id);
let became_pending = !self.pending.swap(true, Ordering::AcqRel);
became_pending || source_added
}
pub(crate) fn pending_terminal_title_sources(&self) -> HashSet<PaneId> {
- self.request
+ self.state
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
+ .request
.terminal_title_sources
.clone()
}
pub(crate) fn take(&self) -> RenderRequest {
- let mut request = self
- .request
+ let mut state = self
+ .state
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
self.pending.store(false, Ordering::Release);
- std::mem::take(&mut *request)
+ std::mem::take(&mut state.request)
}
}
@@ -116,7 +119,7 @@ mod tests {
assert!(signal.request_pty(first));
assert!(!signal.request_pty(first));
- assert!(signal.request_pty(second));
+ assert!(!signal.request_pty(second));
let request = signal.take();
assert!(!request.generic);
@@ -126,6 +129,31 @@ mod tests {
}
#[test]
+ fn hidden_pty_sources_coalesce_to_one_wake() {
+ let signal = RenderSignal::new();
+ signal.set_immediate_pty_sources(HashSet::from([PaneId::from_raw(100)]));
+
+ let wakes = (1..=50)
+ .filter(|pane_id| signal.request_pty(PaneId::from_raw(*pane_id)))
+ .count();
+
+ assert_eq!(wakes, 1);
+ }
+
+ #[test]
+ fn immediate_pty_source_wakes_pending_hidden_work() {
+ let signal = RenderSignal::new();
+ let hidden = PaneId::from_raw(10);
+ let visible = PaneId::from_raw(20);
+ signal.set_immediate_pty_sources(HashSet::from([visible]));
+
+ assert!(signal.request_pty(hidden));
+ assert!(!signal.request_pty(PaneId::from_raw(30)));
+ assert!(signal.request_pty(visible));
+ assert!(!signal.request_pty(visible));
+ }
+
+ #[test]
fn terminal_title_source_wakes_pending_pty_work() {
let signal = RenderSignal::new();
let pane_id = PaneId::from_raw(10);
@@ -158,7 +186,7 @@ mod tests {
let pane_id = PaneId::from_raw(10);
signal.request_generic();
- assert!(signal.request_pty(pane_id));
+ assert!(!signal.request_pty(pane_id));
let request = signal.take();
assert!(request.generic);