Commit 33d439770e90
tarball1 file changed+38-16
@@ -1,7 +1,7 @@
11
//! Launch context. A window goes to the output and tag the user was on when2
-//! its launch began, found by matching its process or an ancestor against3
-//! the programs monolith spawned, then against processes that own a mapped4
-//! window, and last the focus at map time.2
+//! its launch began. Its process and then each ancestor is matched against3
+//! processes that own a mapped window and the programs monolith spawned, the4
+//! nearest match deciding, and the focus at map time is the last resort.55
66
use crate::state::State;77
use monolith_layout::{OutputId, TagId};@@ -31,11 +31,11 @@ impl Launches {
3131
});3232
}3333
34
- pub fn find(&self, pid: u32) -> Option<(Option<OutputId>, TagId)> {35
- lineage(pid).find_map(|pid| {36
- let l = self.0.iter().find(|l| l.pid == pid)?;37
- Some((l.output, l.tag))38
- })34
+ fn get(&self, pid: u32) -> Option<(Option<OutputId>, TagId)> {35
+ self.036
+ .iter()37
+ .find(|l| l.pid == pid && l.at.elapsed() < KEEP)38
+ .map(|l| (l.output, l.tag))3939
}4040
}4141
@@ -45,16 +45,14 @@ fn lineage(pid: u32) -> impl Iterator<Item = u32> {
4545
}4646
4747
impl State {48
- /// Where the window of process `pid` belongs. A program monolith spawned49
- /// comes first, then the nearest ancestor owning a mapped window, which50
- /// includes `pid` itself for its second window, then the focus now.48
+ /// Where the window of process `pid` belongs. The nearest of `pid` and49
+ /// its ancestors that owns a mapped window decides, so a program's later50
+ /// windows follow its first one wherever the user moved it. Failing that51
+ /// the nearest program monolith spawned recently, then the focus now.5152
pub fn launch_of(&self, pid: Option<u32>) -> (Option<OutputId>, TagId) {5253
let Some(pid) = pid else {5354
return self.launch_context();5455
};55
- if let Some(found) = self.services.launches.find(pid) {56
- return found;57
- }5856
let owners: Vec<(u32, OutputId, TagId)> = self5957
.wm6058
.iter()@@ -67,8 +65,12 @@ impl State {
6765
})6866
.collect();6967
lineage(pid)70
- .find_map(|p| owners.iter().find(|o| o.0 == p))71
- .map(|&(_, output, tag)| (Some(output), tag))68
+ .find_map(|p| {69
+ let owned = owners.iter().find(|o| o.0 == p);70
+ owned71
+ .map(|&(_, output, tag)| (Some(output), tag))72
+ .or_else(|| self.services.launches.get(p))73
+ })7274
.unwrap_or_else(|| self.launch_context())7375
}7476
@@ -89,3 +91,23 @@ fn parent(pid: u32) -> Option<u32> {
8991
let rest = stat.get(stat.rfind(')')? + 2..)?;9092
rest.split(' ').nth(1)?.parse().ok()9193
}94
+95
+#[cfg(test)]96
+mod tests {97
+ use super::*;98
+99
+ #[test]100
+ fn a_record_past_its_keep_time_matches_nothing() {101
+ let mut launches = Launches::default();102
+ launches.record(8, None, TagId::default());103
+ let old = Instant::now().checked_sub(KEEP + Duration::from_secs(1));104
+ launches.0.push(Launch {105
+ pid: 7,106
+ output: None,107
+ tag: TagId::default(),108
+ at: old.expect("the clock has run for two minutes"),109
+ });110
+ assert_eq!(launches.get(7), None);111
+ assert!(launches.get(8).is_some());112
+ }113
+}