1
0
Fork 0
forked from wry/wry

accepts_input_at rejects buffer-less surfaces

This commit is contained in:
entailz 2026-05-20 18:48:48 -07:00
parent bb43c238e3
commit 12adb678bb

View file

@ -318,7 +318,7 @@ pub struct WlSurface {
pub content_type: Cell<Option<ContentType>>,
pub drm_feedback: CopyHashMap<ZwpLinuxDmabufFeedbackV1Id, Rc<ZwpLinuxDmabufFeedbackV1>>,
syncobj_surface: CloneCell<Option<Rc<WpLinuxDrmSyncobjSurfaceV1>>>,
destroyed: Cell<bool>,
pub destroyed: Cell<bool>,
commit_timeline: CommitTimeline,
alpha_modifier: CloneCell<Option<Rc<WpAlphaModifierSurfaceV1>>>,
alpha: Cell<Option<f32>>,
@ -1019,6 +1019,7 @@ impl WlSurfaceRequestHandler for WlSurface {
self.unset_dnd_icons();
self.unset_cursors();
self.ext.get().on_surface_destroy()?;
self.destroyed.set(true);
self.destroy_node();
{
let mut children = self.children.borrow_mut();
@ -1029,6 +1030,19 @@ impl WlSurfaceRequestHandler for WlSurface {
}
*children = None;
}
// Capture a close-animation snapshot if the client is destroying the
// surface while it still has a buffer (i.e. without a clean null-attach
// commit first — typical for crash/disconnect paths).
if self.buffer.is_some()
&& let Some(tl) = self.toplevel.get()
&& let Some(snap) = crate::animation::capture_snapshot(&self.client.state, &tl)
{
self.client
.state
.close_snapshots
.borrow_mut()
.push(Rc::new(snap));
}
self.buffer.set(None);
self.reset_shm_textures();
if let Some(xwayland_serial) = self.xwayland_serial.get() {
@ -1041,7 +1055,6 @@ impl WlSurfaceRequestHandler for WlSurface {
self.client.remove_obj(self)?;
self.idle_inhibitors.clear();
self.constraints.take();
self.destroyed.set(true);
Ok(())
}
@ -1238,8 +1251,24 @@ impl WlSurface {
let mut buffer_changed = false;
let mut old_raw_size = None;
let (mut dx, mut dy) = mem::take(&mut pending.offset);
let mut buffer_presence_changed = false;
if let Some(buffer_change) = pending.buffer.take() {
buffer_changed = true;
buffer_presence_changed = buffer_change.is_some() != self.buffer.is_some();
// If the client just attached a null buffer to the main surface of
// a mapped toplevel, capture a snapshot before we drop the buffer
// so the close animation has something to render after teardown.
if buffer_change.is_none()
&& self.buffer.is_some()
&& let Some(tl) = self.toplevel.get()
&& let Some(snap) = crate::animation::capture_snapshot(&self.client.state, &tl)
{
self.client
.state
.close_snapshots
.borrow_mut()
.push(Rc::new(snap));
}
if let Some(buffer) = self.buffer.take() {
old_raw_size = Some(buffer.buffer.buf.rect);
}
@ -1408,6 +1437,16 @@ impl WlSurface {
};
self.is_opaque.set(is_opaque);
}
if buffer_abs_pos_size_changed || buffer_presence_changed {
// Pointer focus depends on whether this surface accepts input.
// It just changed (size shrank/grew, or buffer went from null to
// non-null or vice versa — the latter happens when a client
// dismisses a subsurface by null-attaching while keeping its
// wp_viewport destination). Force a re-evaluation so the pointer
// stack doesn't keep a now-invisible surface focused until the
// next motion event.
self.client.state.tree_changed();
}
let mut tearing_changed = false;
if let Some(tearing) = pending.tearing.take()
&& self.tearing.replace(tearing) != tearing
@ -1597,6 +1636,13 @@ impl WlSurface {
}
fn accepts_input_at(&self, mut x: i32, mut y: i32) -> bool {
// Per the wayland spec, a surface without a buffer is invisible and
// cannot receive input. Without this check, a client that null-buffers
// but keeps a wp_viewport destination set (as foot does for its
// fractional-scaling subsurfaces) would keep an invisible hit-rect.
if self.buffer.is_none() {
return false;
}
let rect = self.buffer_abs_pos.get().at_point(0, 0);
if !rect.contains(x, y) {
return false;