1
0
Fork 0
forked from wry/wry

fix: restore focus after backend visibility resumes

This commit is contained in:
atagen 2026-05-31 17:12:49 +10:00
parent f777b4c521
commit 5db14936e7
2 changed files with 58 additions and 5 deletions

View file

@ -2,7 +2,7 @@ use {
crate::{
ifs::wl_surface::xdg_surface::xdg_toplevel::STATE_SUSPENDED,
it::{
test_error::TestResult,
test_error::{TestErrorExt, TestResult},
test_utils::{
test_ouput_node_ext::TestOutputNodeExt, test_toplevel_node_ext::TestToplevelNodeExt,
},
@ -10,7 +10,7 @@ use {
},
},
isnt::std_1::collections::IsntHashSetExt,
std::rc::Rc,
std::{rc::Rc, time::Duration},
};
testcase!();
@ -19,6 +19,7 @@ async fn test(run: Rc<TestRun>) -> TestResult {
let ds = run.create_default_setup().await?;
let client = run.create_client().await?;
let default_seat = client.get_default_seat().await?;
let win1 = client.create_window().await?;
win1.set_color(255, 0, 0, 255);
@ -44,5 +45,23 @@ async fn test(run: Rc<TestRun>) -> TestResult {
client.sync().await;
tassert!(win2.tl.core.states.borrow().not_contains(&STATE_SUSPENDED));
let leaves = default_seat.kb.leave.expect()?;
let enters = default_seat.kb.enter.expect()?;
run.cfg.set_idle(Duration::from_micros(100))?;
run.cfg.set_idle_grace_period(Duration::from_secs(0))?;
run.state.wheel.timeout(3).await?;
client.sync().await;
tassert!(win2.tl.core.states.borrow().contains(&STATE_SUSPENDED));
let leave = leaves.next().with_context(|| "no leave on suspend")?;
tassert_eq!(leave.surface, win2.surface.id);
ds.mouse.rel(1.0, 1.0);
client.sync().await;
tassert!(win2.tl.core.states.borrow().not_contains(&STATE_SUSPENDED));
let enter = enters.next().with_context(|| "no enter on restore")?;
tassert_eq!(enter.surface, win2.surface.id);
Ok(())
}

View file

@ -8,18 +8,25 @@ use {
renderer::Renderer,
state::State,
tree::{
FindTreeResult, FindTreeUsecase, FoundNode, Node, NodeId, NodeLayerLink, NodeLocation,
OutputNode, StackedNode, TileDragDestination, WorkspaceDragDestination,
Direction, FindTreeResult, FindTreeUsecase, FoundNode, Node, NodeId, NodeLayerLink,
NodeLocation, OutputNode, StackedNode, TileDragDestination, WorkspaceDragDestination,
WorkspaceNodeId, walker::NodeVisitor,
},
utils::{copyhashmap::CopyHashMap, linkedlist::LinkedList},
},
std::{cell::Cell, ops::Deref, rc::Rc},
std::{
cell::{Cell, RefCell},
mem,
ops::Deref,
rc::{Rc, Weak},
},
};
pub struct DisplayNode {
pub id: NodeId,
pub extents: Cell<Rect>,
visible: Cell<bool>,
suspend_restore_kb_foci: RefCell<Vec<(Rc<WlSeatGlobal>, Weak<dyn Node>)>>,
pub outputs: CopyHashMap<ConnectorId, Rc<OutputNode>>,
pub stacked: Rc<LinkedList<Rc<dyn StackedNode>>>,
pub stacked_above_layers: Rc<LinkedList<Rc<dyn StackedNode>>>,
@ -31,6 +38,8 @@ impl DisplayNode {
let slf = Self {
id,
extents: Default::default(),
visible: Default::default(),
suspend_restore_kb_foci: Default::default(),
outputs: Default::default(),
stacked: Default::default(),
stacked_above_layers: Default::default(),
@ -71,6 +80,17 @@ impl DisplayNode {
pub fn update_visible(&self, state: &State) {
let visible = state.root_visible();
let was_visible = self.visible.replace(visible);
if !visible && was_visible {
let mut foci = self.suspend_restore_kb_foci.borrow_mut();
foci.clear();
for seat in state.globals.seats.lock().values() {
let node = seat.get_keyboard_node();
if node.node_id() != self.id {
foci.push((seat.clone(), Rc::downgrade(&node)));
}
}
}
for output in self.outputs.lock().values() {
output.update_visible();
}
@ -82,6 +102,20 @@ impl DisplayNode {
for seat in state.globals.seats.lock().values() {
seat.set_visible(visible);
}
if visible && !was_visible {
for (seat, node) in mem::take(&mut *self.suspend_restore_kb_foci.borrow_mut()) {
if seat.get_keyboard_node().node_id() == self.id {
if let Some(node) = node.upgrade()
&& node.node_visible()
{
seat.focus_node(node);
} else {
seat.get_fallback_output()
.take_keyboard_navigation_focus(&seat, Direction::Unspecified);
}
}
}
}
if visible {
state.damage(self.extents.get());
}