smarter overlay spawning

This commit is contained in:
galister 2026-07-05 04:14:07 +09:00
parent 1cbae4c3ad
commit a9c6599d63
3 changed files with 171 additions and 27 deletions

View File

@ -125,6 +125,7 @@ pub struct WvrServerState {
mouse_freeze: Instant,
window_to_overlay: HashMap<window::WindowHandle, OverlayID>,
overlay_to_window: SecondaryMap<OverlayID, window::WindowHandle>,
process_overlays: HashMap<process::ProcessHandle, Vec<OverlayID>>,
}
#[derive(Clone, Copy, PartialEq)]
@ -274,6 +275,7 @@ impl WvrServerState {
mouse_freeze: Instant::now(),
window_to_overlay: HashMap::new(),
overlay_to_window: SecondaryMap::new(),
process_overlays: HashMap::new(),
})
}
@ -379,6 +381,12 @@ impl WvrServerState {
.unwrap_or_else(|| format!("P{}", client.pid))
.into();
let spawn_pos = wvr_server
.last_process_overlay(process_handle)
.map_or(SpawnPos::Spread, |oid| {
SpawnPos::Parent(OverlaySelector::Id(oid))
});
let window_handle = wvr_server.wm.create_window(
toplevel.clone(),
process_handle,
@ -436,7 +444,7 @@ impl WvrServerState {
app.tasks.enqueue(TaskType::Overlay(OverlayTask::Spawn(
OverlaySelector::Nothing,
SpawnPos::Spread,
spawn_pos,
Box::new(move |app: &mut AppState| {
create_wl_window_overlay(
title,
@ -469,11 +477,31 @@ impl WvrServerState {
continue;
};
let process_handle = wvr_server
.wm
.windows
.get(&window_handle)
.map(|window| window.process);
if let Some(oid) = wvr_server.window_to_overlay.remove(&window_handle) {
app.tasks.enqueue(TaskType::Overlay(OverlayTask::Drop(
OverlaySelector::Id(oid),
)));
wvr_server.overlay_to_window.remove(oid);
if let Some(process_handle) = process_handle.as_ref() {
let mut empty = false;
if let Some(overlays) =
wvr_server.process_overlays.get_mut(process_handle)
{
overlays.retain(|other| *other != oid);
empty = overlays.is_empty();
}
if empty {
wvr_server.process_overlays.remove(process_handle);
}
}
}
wvr_server.wm.remove_window(window_handle);
@ -566,6 +594,19 @@ impl WvrServerState {
pub fn overlay_added(&mut self, oid: OverlayID, window: window::WindowHandle) {
self.overlay_to_window.insert(oid, window);
self.window_to_overlay.insert(window, oid);
if let Some(process_handle) = self.wm.windows.get(&window).map(|window| window.process) {
let overlays = self.process_overlays.entry(process_handle).or_default();
overlays.retain(|other| *other != oid);
overlays.push(oid);
}
}
fn last_process_overlay(&self, process_handle: process::ProcessHandle) -> Option<OverlayID> {
self.process_overlays
.get(&process_handle)
.and_then(|overlays| overlays.last())
.copied()
}
pub fn process_removed(&mut self, tasks: &mut TaskContainer, process: process::ProcessHandle) {
@ -591,6 +632,8 @@ impl WvrServerState {
for hnd in &to_remove {
self.wm.windows.remove(hnd);
}
self.process_overlays.remove(&process);
}
pub fn get_overlay_id(&self, window: window::WindowHandle) -> Option<OverlayID> {

View File

@ -16,7 +16,7 @@ use wlx_common::{
use crate::{
FRAME_COUNTER,
backend::task::{OverlayTask, ToggleMode},
backend::task::{CreateOverlayTask, OverlayTask, SpawnPos, ToggleMode},
config::save_state,
overlays::{
anchor::{create_anchor, create_grab_help},
@ -34,7 +34,7 @@ use crate::{
backend::{OverlayEventData, OverlayMeta},
set::OverlayWindowSet,
snap_upright,
window::{OverlayCategory, OverlayWindowData},
window::{OverlayCategory, OverlayWindowData, save_transform, spawn_transform_from_parent},
},
};
@ -345,24 +345,8 @@ where
log::warn!("Overlay not found for task: {sel:?}");
}
}
OverlayTask::Spawn(sel, pos, f) => {
let None = self.mut_by_selector(&sel) else {
log::debug!("Could not create {sel:?}: exists");
return Ok(());
};
let Some(overlay_config) = f(app) else {
log::debug!("Could not create {sel:?}: empty config");
return Ok(());
};
self.add(
OverlayWindowData {
birthframe: FRAME_COUNTER.load(Ordering::Relaxed),
..OverlayWindowData::from_config(overlay_config)
},
app,
);
OverlayTask::Spawn(sel, spawn_pos, f) => {
self.spawn_overlay(app, sel, spawn_pos, f)?;
}
OverlayTask::Drop(sel) => {
if let Some(o) = self.mut_by_selector(&sel)
@ -402,6 +386,35 @@ where
}
Ok(())
}
fn spawn_overlay(
&mut self,
app: &mut AppState,
sel: OverlaySelector,
spawn_pos: SpawnPos,
f: Box<CreateOverlayTask>,
) -> anyhow::Result<()> {
let None = self.mut_by_selector(&sel) else {
log::debug!("Could not spawn {sel:?}: exists");
return Ok(());
};
let Some(overlay_config) = f(app) else {
log::debug!("Could not spawn {sel:?}: empty config");
return Ok(());
};
self.add_with_spawn_pos(
OverlayWindowData {
birthframe: FRAME_COUNTER.load(Ordering::Relaxed),
..OverlayWindowData::from_config(overlay_config)
},
app,
spawn_pos,
);
Ok(())
}
}
const SAVED_ATTRIBS: [BackendAttrib; 3] = [
@ -734,7 +747,16 @@ impl<T> OverlayWindowManager<T> {
.map(|(k, _)| k)
}
pub fn add(&mut self, mut overlay: OverlayWindowData<T>, app: &mut AppState) -> OverlayID {
pub fn add(&mut self, overlay: OverlayWindowData<T>, app: &mut AppState) -> OverlayID {
self.add_with_spawn_pos(overlay, app, SpawnPos::Fixed)
}
fn add_with_spawn_pos(
&mut self,
mut overlay: OverlayWindowData<T>,
app: &mut AppState,
spawn_pos: SpawnPos,
) -> OverlayID {
while self.lookup(&overlay.config.name).is_some() {
log::error!(
"An overlay with name {} already exists. Deduplicating, but things may break!",
@ -778,6 +800,7 @@ impl<T> OverlayWindowManager<T> {
if !shown && show_on_spawn {
log::debug!("activating {name} due to show_on_spawn");
self.overlays[oid].config.activate(app);
self.apply_spawn_pos(app, oid, spawn_pos);
}
if !internal && let Err(e) = self.overlays_changed(app) {
log::error!("Error while adding overlay: {e:?}");
@ -788,6 +811,71 @@ impl<T> OverlayWindowManager<T> {
oid
}
fn apply_spawn_pos(&mut self, app: &mut AppState, oid: OverlayID, spawn_pos: SpawnPos) {
match spawn_pos {
SpawnPos::Fixed => {}
SpawnPos::Spread => {
let Some(parent_id) = self.spread_parent_for(oid) else {
return;
};
let parent = OverlaySelector::Id(parent_id);
let _ = self.offset_spawn_from_parent(app, oid, &parent);
}
SpawnPos::Parent(parent) => {
let _ = self.offset_spawn_from_parent(app, oid, &parent);
}
}
}
// TODO: this just uses the last spawned overlay as parent, can probably do better?
fn spread_parent_for(&self, oid: OverlayID) -> Option<OverlayID> {
self.overlays
.iter()
.filter(|(id, overlay)| {
*id != oid
&& overlay.config.active_state.is_some()
&& !matches!(
overlay.config.category,
OverlayCategory::Internal
| OverlayCategory::Keyboard
| OverlayCategory::Dashboard
)
})
.max_by_key(|(_, overlay)| overlay.birthframe)
.map(|(id, _)| id)
}
fn offset_spawn_from_parent(
&mut self,
app: &mut AppState,
oid: OverlayID,
parent: &OverlaySelector,
) -> Option<()> {
let parent_id = self.id_by_selector(parent)?;
if parent_id == oid {
return None;
}
let parent_transform = self
.overlays
.get(parent_id)?
.config
.active_state
.as_ref()?
.transform;
let spawn_transform = spawn_transform_from_parent(&parent_transform, &app.input_state.hmd);
let overlay = self.overlays.get_mut(oid)?;
let state = overlay.config.active_state.as_mut()?;
state.transform = spawn_transform;
save_transform(state, app);
overlay.config.dirty = true;
Some(())
}
pub fn switch_or_toggle_set(&mut self, app: &mut AppState, set: usize) {
let new_set = if self.current_set.iter().any(|cur| *cur == set) {
None

View File

@ -310,10 +310,23 @@ pub fn save_transform(state: &mut OverlayWindowState, app: &mut AppState) {
state.saved_transform = Some(parent_transform.inverse() * state.transform);
}
pub fn spawn_transform_from_parent(parent: &Affine3A, _hmd: &Affine3A) -> Affine3A {
let local_offset = Vec3A::new(0.08, -0.08, 0.06);
let world_offset = parent.x_axis * local_offset.x - parent.y_axis * local_offset.y
+ parent.z_axis * local_offset.z;
pub fn spawn_transform_from_parent(parent: &Affine3A, hmd: &Affine3A) -> Affine3A {
const LEFT: f32 = 0.08;
const DOWN: f32 = 0.08;
const CLOSER_TO_HMD: f32 = 0.06;
Affine3A::from_translation((parent.translation + world_offset).into())
let x_axis = parent.x_axis.normalize();
let y_axis = parent.y_axis.normalize();
let z_axis = parent.z_axis.normalize();
let to_hmd = hmd.translation - parent.translation;
let toward_hmd = if z_axis.dot(to_hmd) >= 0.0 {
z_axis
} else {
-z_axis
};
let world_offset = -x_axis * LEFT - y_axis * DOWN + toward_hmd * CLOSER_TO_HMD;
let mut transform = *parent;
transform.translation += world_offset;
transform
}