#4380

Clicks outside a client's input region (but inside window geometry) activate the window instead of falling through

OpenMuriloX06opened 2mo ago

Description

When a window (especially a floating transparent overlay) sets a non-rectangular wl_surface input region, clicks that land inside the window's geometry but outside that input region currently activate / focus the window and consume the click. They do not fall through to windows underneath.

This breaks desktop/taskbar-style overlay games and other clients that intentionally punch holes in their input region for click-through areas (while keeping opaque content interactive).

Related ideas (whole-window click-through as a rule): #2743, #4255. This issue is about honoring the client's own input region for activation, not forcing an entire window to ignore input via config.

Reproducer

Reference game: TBH: Task Bar Hero (Steam AppID 3678970), running under Proton with the layered-overlay alpha path (WINE_LAYERED_OVERLAY_ALPHA=1, available in current GE-Proton). That path:

  1. Gives the X11 window a real ARGB visual (transparency composites correctly).
  2. Sets a per-pixel ShapeInput from the rendered content so only opaque pixels receive input.

Xwayland translates that into a Wayland wl_surface.set_input_region. On Hyprland, clicks on the transparent areas pass through to windows behind; on niri they do not — the overlay window is activated instead.

I also verified with a minimal X11 test window (ARGB + XShapeCombineRectangles(..., ShapeInput, ...)) under niri + xwayland-satellite: input region works for event delivery to the client, but a click on the excluded area still activates the floating window and does not reach the window underneath.

Why (code path)

In src/layout/tile.rs, Tile::hit does roughly:

if self.is_in_input_region(point) {
    Some(HitType::Input { ... })
} else if self.is_in_activation_region(point) {
    Some(HitType::Activate { ... })  // full tile rect, including window geometry
} else {
    None
}

HitType::Activate is useful for borders / chrome outside the client surface. But for points inside the window's own geometry, the client has already said "this pixel is not mine" via the input region. Treating that as an activate-hit means the floating window always "wins" over tiled windows behind it, so the click never falls through in workspace.window_under / floating hit testing.

Suggested behavior

  • Inside window geometry, outside input region → return None from hit testing (fall through to windows below).
  • Outside window geometry but still in the tile (border, tab indicator, etc.) → keep current HitType::Activate behavior.

A small local patch that implements exactly that works as expected for the overlay game:

} else if self.is_in_activation_region(point) {
    let window_rect = Rectangle::new(self.window_loc(), self.window_size());
    if window_rect.contains(point) {
        return None; // honor client's input-region holes
    }
    Some(HitType::Activate {
        is_tab_indicator: false,
    })
}

Tested on niri v26.04-45-g205661f5 with the floating Task Bar Hero window: transparent areas pass clicks to the editor behind; opaque sprites / UI still interact with the game; borders (when present) still activate.

Alternatives / design questions

  1. Always fall through for in-window out-of-input-region hits (behavior above) — matches what clients that set input regions usually want, and matches Hyprland's practical behavior for this case.
  2. Gate it behind a window rule (e.g. click-through-outside-input-region true / honor-input-region-for-activation true) if changing the default is a concern for existing workflows.
  3. Broader "force whole window click-through" rules from #2743 / #4255 remain useful for clients that don't set an input region, but they don't replace this.

Happy to open a PR with the patch + a short test note if this direction looks good.

System

  • niri: 26.04 (v26.04-45-g205661f5)
  • Wayland session, X11 clients via xwayland-satellite
  • Overlay client: Steam / Proton (GE) + Task Bar Hero

Activity

10
Sempyos2mo ago

please provide a video of this happening and replicate it on sway and cosmic-comp

To me this just sounds to be another input passthrough feature request and not an issue

Sempyos2mo ago

please provide a video of this happening and replicate it on sway and cosmic-comp

To me this just sounds to be another input passthrough feature request and not an issue

Sempyos2mo ago

please provide a video of this happening and replicate it on sway and cosmic-comp

To me this just sounds to be another input passthrough feature request and not an issue

YaLTeR2mo ago

I guess it's fine to do, but it can't be as simple as your proposal. For example if the border enabled and is drawn behind the window (the default), then it would be strange if clicking there (visually on the border) would fall through instead of activating the window. It needs to take this stuff into account

YaLTeR2mo ago

I guess it's fine to do, but it can't be as simple as your proposal. For example if the border enabled and is drawn behind the window (the default), then it would be strange if clicking there (visually on the border) would fall through instead of activating the window. It needs to take this stuff into account

YaLTeR2mo ago

I guess it's fine to do, but it can't be as simple as your proposal. For example if the border enabled and is drawn behind the window (the default), then it would be strange if clicking there (visually on the border) would fall through instead of activating the window. It needs to take this stuff into account

MuriloX062mo ago

Thanks for taking a look, that makes sense! I've updated #4381 to take this into account.

The hit now only falls through when nothing is drawn behind the window at that point:

  • If the border or the focus ring is enabled for the window and draw-border-with-background resolves to true (the default for windows without SSD), clicks inside the window geometry keep activating the window as before — the solid background visually covers the input-region hole, so falling through there would indeed be strange.
  • The fall-through only happens when there is no background drawn behind the window (border/focus ring off, or draw-border-with-background false).
  • Points outside the window geometry (the border ring itself, etc.) still activate as before.

Happy to adjust the condition further if you'd prefer something else (e.g. treating the focus ring differently since it's only drawn on the focused window).

MuriloX062mo ago

Thanks for taking a look, that makes sense! I've updated #4381 to take this into account.

The hit now only falls through when nothing is drawn behind the window at that point:

  • If the border or the focus ring is enabled for the window and draw-border-with-background resolves to true (the default for windows without SSD), clicks inside the window geometry keep activating the window as before — the solid background visually covers the input-region hole, so falling through there would indeed be strange.
  • The fall-through only happens when there is no background drawn behind the window (border/focus ring off, or draw-border-with-background false).
  • Points outside the window geometry (the border ring itself, etc.) still activate as before.

Happy to adjust the condition further if you'd prefer something else (e.g. treating the focus ring differently since it's only drawn on the focused window).

MuriloX062mo ago

Thanks for taking a look, that makes sense! I've updated #4381 to take this into account.

The hit now only falls through when nothing is drawn behind the window at that point:

  • If the border or the focus ring is enabled for the window and draw-border-with-background resolves to true (the default for windows without SSD), clicks inside the window geometry keep activating the window as before — the solid background visually covers the input-region hole, so falling through there would indeed be strange.
  • The fall-through only happens when there is no background drawn behind the window (border/focus ring off, or draw-border-with-background false).
  • Points outside the window geometry (the border ring itself, etc.) still activate as before.

Happy to adjust the condition further if you'd prefer something else (e.g. treating the focus ring differently since it's only drawn on the focused window).

titaniumtraveler2mo ago

I stumbled over the same bug inside of niri::State::on_pointer_button(). Relevant to note would also be that in case that Mod is pressed (like for an interactive move/resize), the pointer should probably interact with the window only considering the actual window geometry.

Which means the specific semantics are probably somewhat complicated. (Especially if we want to also consider which pointer surface to use, whether to apply pointer constraints etc)