diff --git a/src/ifs/wl_seat/event_handling.rs b/src/ifs/wl_seat/event_handling.rs index 06a309cf..11a3b778 100644 --- a/src/ifs/wl_seat/event_handling.rs +++ b/src/ifs/wl_seat/event_handling.rs @@ -503,8 +503,10 @@ impl WlSeatGlobal { }; self.surface_pointer_event(0, surface, |p| p.send_button(serial, 0, button, state)); self.surface_pointer_frame(surface); - if pressed && surface.accepts_kb_focus() { - self.focus_node(surface.clone()); + if pressed { + if let Some(node) = surface.get_focus_node(self.id) { + self.focus_node(node); + } } } } diff --git a/src/ifs/wl_surface.rs b/src/ifs/wl_surface.rs index 901341ac..f518d8a9 100644 --- a/src/ifs/wl_surface.rs +++ b/src/ifs/wl_surface.rs @@ -163,8 +163,8 @@ trait SurfaceExt { None } - fn accepts_kb_focus(&self) -> bool { - true + fn focus_node(&self) -> Option> { + None } } @@ -266,10 +266,11 @@ impl WlSurface { Ok(cursor) } - pub fn accepts_kb_focus(&self) -> bool { + pub fn get_focus_node(&self, seat: SeatId) -> Option> { match self.toplevel.get() { - Some(tl) => tl.tl_accepts_keyboard_focus(), - _ => self.ext.get().accepts_kb_focus(), + Some(tl) if tl.tl_accepts_keyboard_focus() => tl.tl_focus_child(seat), + Some(_) => None, + _ => self.ext.get().focus_node(), } } diff --git a/src/ifs/wl_surface/wl_subsurface.rs b/src/ifs/wl_surface/wl_subsurface.rs index 49b7d07a..831155d6 100644 --- a/src/ifs/wl_surface/wl_subsurface.rs +++ b/src/ifs/wl_surface/wl_subsurface.rs @@ -296,10 +296,6 @@ impl SurfaceExt for WlSubsurface { fn into_subsurface(self: Rc) -> Option> { Some(self) } - - fn accepts_kb_focus(&self) -> bool { - self.parent.accepts_kb_focus() - } } #[derive(Debug, Error)] diff --git a/src/ifs/wl_surface/xdg_surface.rs b/src/ifs/wl_surface/xdg_surface.rs index f606b7d7..c1d908be 100644 --- a/src/ifs/wl_surface/xdg_surface.rs +++ b/src/ifs/wl_surface/xdg_surface.rs @@ -360,10 +360,6 @@ impl SurfaceExt for XdgSurface { fn extents_changed(&self) { self.update_extents(); } - - fn accepts_kb_focus(&self) -> bool { - self.role.get() == XdgSurfaceRole::XdgToplevel - } } #[derive(Debug, Error)] diff --git a/src/ifs/wl_surface/xdg_surface/xdg_toplevel.rs b/src/ifs/wl_surface/xdg_surface/xdg_toplevel.rs index 9603bcc5..0ce99f04 100644 --- a/src/ifs/wl_surface/xdg_surface/xdg_toplevel.rs +++ b/src/ifs/wl_surface/xdg_surface/xdg_toplevel.rs @@ -6,7 +6,7 @@ use { cursor::KnownCursor, fixed::Fixed, ifs::{ - wl_seat::{NodeSeatState, WlSeatGlobal}, + wl_seat::{NodeSeatState, SeatId, WlSeatGlobal}, wl_surface::xdg_surface::{XdgSurface, XdgSurfaceError, XdgSurfaceExt}, }, leaks::Tracker, @@ -437,10 +437,6 @@ impl ToplevelNode for XdgToplevel { &self.toplevel_data } - fn tl_default_focus_child(&self) -> Option> { - Some(self.xdg.surface.clone()) - } - fn tl_set_active(&self, active: bool) { let changed = { let mut states = self.states.borrow_mut(); @@ -456,6 +452,10 @@ impl ToplevelNode for XdgToplevel { } } + fn tl_focus_child(&self, _seat: SeatId) -> Option> { + Some(self.xdg.surface.clone()) + } + fn tl_set_workspace(self: Rc, ws: &Rc) { self.toplevel_data.workspace.set(Some(ws.clone())); self.xdg.set_workspace(ws); diff --git a/src/ifs/wl_surface/xwindow.rs b/src/ifs/wl_surface/xwindow.rs index 85e92ad8..5562a7d9 100644 --- a/src/ifs/wl_surface/xwindow.rs +++ b/src/ifs/wl_surface/xwindow.rs @@ -4,7 +4,7 @@ use { cursor::KnownCursor, fixed::Fixed, ifs::{ - wl_seat::{NodeSeatState, WlSeatGlobal}, + wl_seat::{NodeSeatState, SeatId, WlSeatGlobal}, wl_surface::{SurfaceExt, SurfaceRole, WlSurface, WlSurfaceError}, }, rect::Rect, @@ -383,10 +383,6 @@ impl ToplevelNode for Xwindow { &self.toplevel_data } - fn tl_default_focus_child(&self) -> Option> { - Some(self.surface.clone()) - } - fn tl_accepts_keyboard_focus(&self) -> bool { self.data.info.never_focus.get().not() && self.data.info.input_model.get() != XInputModel::None @@ -400,6 +396,10 @@ impl ToplevelNode for Xwindow { .push(XWaylandEvent::Activate(self.data.clone())); } + fn tl_focus_child(&self, _seat: SeatId) -> Option> { + Some(self.surface.clone()) + } + fn tl_change_extents(self: Rc, rect: &Rect) { // log::info!("xwin {} change_extents {:?}", self.data.window_id, rect); let old = self.data.info.extents.replace(*rect); diff --git a/src/ifs/wl_surface/zwlr_layer_surface_v1.rs b/src/ifs/wl_surface/zwlr_layer_surface_v1.rs index 7c93bac9..c47faa4d 100644 --- a/src/ifs/wl_surface/zwlr_layer_surface_v1.rs +++ b/src/ifs/wl_surface/zwlr_layer_surface_v1.rs @@ -356,8 +356,12 @@ impl SurfaceExt for ZwlrLayerSurfaceV1 { } } - fn accepts_kb_focus(&self) -> bool { - self.keyboard_interactivity.get() != KI_NONE + fn focus_node(&self) -> Option> { + if self.keyboard_interactivity.get() != KI_NONE { + Some(self.surface.clone()) + } else { + None + } } } diff --git a/src/it/test_backend.rs b/src/it/test_backend.rs index b6e56cd0..87b2f272 100644 --- a/src/it/test_backend.rs +++ b/src/it/test_backend.rs @@ -234,6 +234,19 @@ impl Connector for TestConnector { } } +pub struct TestMouseClick { + pub mouse: Rc, + pub button: u32, +} + +impl Drop for TestMouseClick { + fn drop(&mut self) { + self.mouse + .common + .event(InputEvent::Button(self.button, KeyState::Released)); + } +} + pub struct TestBackendMouse { pub common: TestInputDeviceCommon, pub transform_matrix: Cell, @@ -252,6 +265,15 @@ impl TestBackendMouse { dy_unaccelerated: Fixed::from_f64(dy), }) } + + pub fn click(self: &Rc, button: u32) -> TestMouseClick { + self.common + .event(InputEvent::Button(button, KeyState::Pressed)); + TestMouseClick { + mouse: self.clone(), + button, + } + } } pub struct TestBackendKb { diff --git a/src/it/test_client.rs b/src/it/test_client.rs index f80272fd..c9e28d88 100644 --- a/src/it/test_client.rs +++ b/src/it/test_client.rs @@ -79,7 +79,8 @@ impl TestClient { } pub async fn sync(&self) { - self.tran.sync().await + self.run.sync().await; + self.tran.sync().await; } pub async fn take_screenshot(&self) -> Result, TestError> { diff --git a/src/it/test_ifs/test_pointer.rs b/src/it/test_ifs/test_pointer.rs index 66dd63cd..05249f84 100644 --- a/src/it/test_ifs/test_pointer.rs +++ b/src/it/test_ifs/test_pointer.rs @@ -3,7 +3,7 @@ use { ifs::wl_seat::wl_pointer::WlPointer, it::{ test_error::TestResult, test_object::TestObject, test_transport::TestTransport, - testrun::ParseFull, + test_utils::test_expected_event::TEEH, testrun::ParseFull, }, utils::{buffd::MsgParser, clonecell::CloneCell}, wire::{wl_pointer::*, WlPointerId}, @@ -16,6 +16,9 @@ pub struct TestPointer { pub tran: Rc, pub server: CloneCell>>, pub destroyed: Cell, + pub leave: TEEH, + pub enter: TEEH, + pub motion: TEEH, } impl TestPointer { @@ -27,17 +30,20 @@ impl TestPointer { } fn handle_enter(&self, parser: MsgParser<'_, '_>) -> TestResult { - let _ev = Enter::parse_full(parser)?; + let ev = Enter::parse_full(parser)?; + self.enter.push(ev); Ok(()) } fn handle_leave(&self, parser: MsgParser<'_, '_>) -> TestResult { - let _ev = Leave::parse_full(parser)?; + let ev = Leave::parse_full(parser)?; + self.leave.push(ev); Ok(()) } fn handle_motion(&self, parser: MsgParser<'_, '_>) -> TestResult { - let _ev = Motion::parse_full(parser)?; + let ev = Motion::parse_full(parser)?; + self.motion.push(ev); Ok(()) } diff --git a/src/it/test_ifs/test_seat.rs b/src/it/test_ifs/test_seat.rs index 48d2e5e3..9e65b876 100644 --- a/src/it/test_ifs/test_seat.rs +++ b/src/it/test_ifs/test_seat.rs @@ -63,6 +63,9 @@ impl TestSeat { tran: self.tran.clone(), server: Default::default(), destroyed: Default::default(), + leave: Rc::new(Default::default()), + enter: Rc::new(Default::default()), + motion: Rc::new(Default::default()), }); self.tran.add_obj(pointer.clone())?; self.tran.sync().await; diff --git a/src/it/test_ifs/test_shm.rs b/src/it/test_ifs/test_shm.rs index daf6c57a..d58d3a7e 100644 --- a/src/it/test_ifs/test_shm.rs +++ b/src/it/test_ifs/test_shm.rs @@ -1,8 +1,13 @@ use { crate::{ + format::ARGB8888, it::{ - test_error::TestError, test_ifs::test_shm_pool::TestShmPool, test_mem::TestMem, - test_object::TestObject, test_transport::TestTransport, testrun::ParseFull, + test_error::{TestError, TestResult}, + test_ifs::{test_shm_buffer::TestShmBuffer, test_shm_pool::TestShmPool}, + test_mem::TestMem, + test_object::TestObject, + test_transport::TestTransport, + testrun::ParseFull, }, utils::{buffd::MsgParser, clonecell::CloneCell, copyhashmap::CopyHashMap}, wire::{wl_shm::*, WlShmId}, @@ -43,6 +48,11 @@ impl TestShm { Ok(pool) } + pub fn create_buffer(&self, width: i32, height: i32) -> TestResult> { + let pool = self.create_pool((width * height * 4) as _)?; + pool.create_buffer(0, width, height, width * 4, ARGB8888) + } + fn handle_format(&self, parser: MsgParser<'_, '_>) -> Result<(), TestError> { let ev = Format::parse_full(parser)?; self.formats.set(ev.format, ()); diff --git a/src/it/tests.rs b/src/it/tests.rs index b80ca514..42610c4f 100644 --- a/src/it/tests.rs +++ b/src/it/tests.rs @@ -37,6 +37,7 @@ mod t0008_map_focus; mod t0009_tab_focus; mod t0010_fullscreen_focus; mod t0011_set_keymap; +mod t0012_subsurface_focus; pub trait TestCase: Sync { fn name(&self) -> &'static str; @@ -66,5 +67,6 @@ pub fn tests() -> Vec<&'static dyn TestCase> { t0009_tab_focus, t0010_fullscreen_focus, t0011_set_keymap, + t0012_subsurface_focus, } } diff --git a/src/it/tests/t0012_subsurface_focus.rs b/src/it/tests/t0012_subsurface_focus.rs new file mode 100644 index 00000000..d000c86f --- /dev/null +++ b/src/it/tests/t0012_subsurface_focus.rs @@ -0,0 +1,69 @@ +use { + crate::{ + ifs::wl_seat::BTN_LEFT, + it::{ + test_error::{TestErrorExt, TestResult}, + testrun::TestRun, + }, + }, + std::rc::Rc, +}; + +testcase!(); + +/// Test that clicking on a subsurface keeps the toplevel surface focused +async fn test(run: Rc) -> TestResult { + let ds = run.create_default_setup().await?; + ds.mouse.rel(1.0, 1.0); + run.sync().await; + + let client = run.create_client().await?; + let cds = client.get_default_seat().await?; + + let window = client.create_window().await?; + window.map().await?; + window.map().await?; + + let ns = client.comp.create_surface().await?; + let nss = client.sub.get_subsurface(ns.id, window.surface.id).await?; + nss.set_position(100, 100)?; + let buffer = client.shm.create_buffer(100, 100)?; + ns.attach(buffer.id)?; + + run.cfg.set_fullscreen(ds.seat.id(), true)?; + client.sync().await; + window.map().await; + + ds.mouse.rel(-1000.0, -1000.0); + + client.sync().await; + + let motions = cds.pointer.motion.expect()?; + let enters = cds.pointer.enter.expect()?; + let leaves = cds.pointer.leave.expect()?; + + ds.mouse.rel(150.0, 150.0); + + client.sync().await; + + tassert!(motions.next().is_err()); + let leave = leaves.next().with_context(|| "leaves")?; + tassert_eq!(leave.surface, window.surface.id); + let enter = enters.next().with_context(|| "enters")?; + tassert_eq!(enter.surface, ns.id); + + tassert!(leaves.next().is_err()); + tassert!(enters.next().is_err()); + + let kenters = cds.kb.enter.expect()?; + let kleaves = cds.kb.leave.expect()?; + + ds.mouse.click(BTN_LEFT); + + client.sync().await; + + tassert!(kleaves.next().is_err()); + tassert!(kenters.next().is_err()); + + Ok(()) +} diff --git a/src/tree/toplevel.rs b/src/tree/toplevel.rs index 7c213939..91be19a5 100644 --- a/src/tree/toplevel.rs +++ b/src/tree/toplevel.rs @@ -23,7 +23,10 @@ pub trait ToplevelNode: Node { fn tl_into_dyn(self: Rc) -> Rc; fn tl_data(&self) -> &ToplevelData; - fn tl_default_focus_child(&self) -> Option>; + + fn tl_default_focus_child(&self) -> Option> { + None + } fn tl_accepts_keyboard_focus(&self) -> bool { true