diff --git a/src/host.rs b/src/host.rs index 7a0bd7e..488f761 100644 --- a/src/host.rs +++ b/src/host.rs @@ -4,7 +4,7 @@ use std::collections::BTreeSet; use std::io::{ErrorKind, Read, Write}; use std::os::unix::net::{UnixListener, UnixStream}; -use std::path::Path; +use std::path::{Path, PathBuf}; use std::sync::Arc; use std::sync::atomic::{AtomicUsize, Ordering}; use std::time::{Duration, Instant}; @@ -103,6 +103,8 @@ pub struct Host { /// The drag-out in progress. Dropping the source cancels the drag. struct Drag { thumb: u64, + /// Kept here so the drop still delivers if the thumbnail goes away. + path: PathBuf, source: DragSource, icon: WlSurface, icon_viewport: WpViewport, @@ -172,7 +174,9 @@ pub fn run() -> Result<()> { .run(Duration::from_millis(500), &mut state, |state| { let host = state.preview.as_mut().expect("host state"); host.tidy(); - let busy = host.thumbs.len() + host.writers.load(Ordering::SeqCst); + let busy = host.thumbs.len() + + host.writers.load(Ordering::SeqCst) + + usize::from(host.drag.is_some()); if host.core.should_exit(busy, Instant::now()) { signal.stop(); } @@ -281,10 +285,7 @@ impl Host { let Some(drag) = self.drag.as_ref().filter(|d| d.source.inner() == source) else { return; }; - let Some(thumb) = self.thumbs.iter().find(|t| t.id == drag.thumb) else { - return; - }; - let path = thumb.path.clone(); + let path = drag.path.clone(); let writers = Arc::clone(&self.writers); writers.fetch_add(1, Ordering::SeqCst); std::thread::spawn(move || { @@ -394,6 +395,7 @@ fn start_drag(state: &mut State, i: usize, start: DragStart, qh: &QueueHandle Result<()> { let open: Vec = (0..host.thumbs.len()) .filter(|&i| host.thumbs[i].output == output.geom.name && !host.thumbs[i].is_closing()) .collect(); - for &i in open.iter().take(stack::excess(open.len())) { - host.thumbs[i].slide_out(&qh); + let dragging: Vec = open.iter().map(|&i| host.thumbs[i].is_dragging()).collect(); + for k in stack::evictions(&dragging) { + host.thumbs[open[k]].slide_out(&qh); } host.restack(); diff --git a/src/stack.rs b/src/stack.rs index 0f5d25b..a378004 100644 --- a/src/stack.rs +++ b/src/stack.rs @@ -46,6 +46,17 @@ pub fn excess(count: usize) -> usize { count.saturating_sub(MAX) } +/// Which thumbnails to evict, given whether each open one (oldest first) is +/// being dragged. A dragged one is skipped: evicting it would leave the drop +/// with nothing to deliver. +pub fn evictions(dragging_oldest_first: &[bool]) -> Vec { + let count = excess(dragging_oldest_first.len()); + (0..dragging_oldest_first.len()) + .filter(|&i| !dragging_oldest_first[i]) + .take(count) + .collect() +} + /// What a pointer release on a thumbnail means. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Release { @@ -191,6 +202,16 @@ mod tests { assert_eq!(bottom_margins(&[124, 124, 220]), vec![16, 152, 288]); } + #[test] + fn eviction_skips_a_dragged_thumbnail() { + // Oldest first; `true` = being dragged. + let none = [false; 6]; + assert_eq!(evictions(&none), vec![0]); + let oldest_dragged = [true, false, false, false, false, false]; + assert_eq!(evictions(&oldest_dragged), vec![1]); + assert_eq!(evictions(&[false; 5]), Vec::::new()); + } + #[test] fn eviction_beyond_five() { assert_eq!(excess(5), 0); diff --git a/src/thumbnail.rs b/src/thumbnail.rs index 332e5ea..71ca960 100644 --- a/src/thumbnail.rs +++ b/src/thumbnail.rs @@ -240,6 +240,10 @@ impl Thumbnail { self.closing && (!self.visible() || anim_over(self.anim, Instant::now())) } + pub fn is_dragging(&self) -> bool { + self.dragging + } + fn visible(&self) -> bool { !self.hidden && !self.dragging }