feat: select overlay windows with the mouse cursor

Moving the cursor over an overlay row now previews that window, the
same as dragging a finger across it; sliding off every row keeps the
last preview. Clicks while the overlay is up do nothing and never
reach clients, and releasing Alt commits the preview.
This commit is contained in:
Andrew Hamilton 2026-09-16 22:00:30 +10:00
parent d91cc8ba8d
commit a4e5633a27
2 changed files with 142 additions and 16 deletions

View file

@ -4962,6 +4962,91 @@ def test_overlay_touch_drag(session):
assert session.active() == drag_target
def test_overlay_pointer_hover(session):
"""Moving the cursor over the overlay while Alt is held previews rows
like a touch drag (no click needed); motion off every row sticks to
the last preview; clicks are swallowed (no new selection, no active
change, nothing reaches the client); Alt release commits."""
session.wait_any_active()
session.wait_surface_mapped(0)
session.wait_surface_mapped(1)
session.mods(ALT)
try:
path = session.dump_host()
w, h, _ = session.ppm_rgb(path)
cx = w // 2
# Locate both rows by warping down the middle.
rows = {}
deadline = time.monotonic() + 25.0
y = 0
while time.monotonic() < deadline and len(rows) < 2 and y < h:
before = len(re.findall(r"\[pointer\] select app=", session.read_log()))
session.warp(cx, y)
time.sleep(0.18)
m = re.findall(r"\[pointer\] select app=(\d+)", session.read_log())
if len(m) > before and m[-1] not in rows:
rows[m[-1]] = y
y += 20
assert len(rows) == 2, f"hover sweep found rows {rows}"
yA, yB = sorted(rows.values())
# Hover from the upper row into the lower one.
session.warp(cx, yA)
time.sleep(0.3)
session.warp(cx, yB)
time.sleep(0.3)
sels = re.findall(r"\[pointer\] select app=(\d+)", session.read_log())
assert len(sels) >= 2 and sels[-2] != sels[-1], \
f"hover never re-targeted: {sels[-2:]}"
hover_target = int(sels[-1])
# Motion off every row keeps the last preview.
n0 = len(re.findall(r"\[pointer\] select app=", session.read_log()))
session.warp(5, 5)
time.sleep(0.3)
n1 = len(re.findall(r"\[pointer\] select app=", session.read_log()))
assert n1 == n0, "off-row hover changed selection"
# Clicks are swallowed: no selection, no active change.
a0 = session.active_count()
session.button(BTN_LEFT, 1)
time.sleep(0.15)
session.button(BTN_LEFT, 0)
time.sleep(0.3)
n2 = len(re.findall(r"\[pointer\] select app=", session.read_log()))
assert n2 == n1, "click while overlay up selected a window"
assert session.active_count() == a0, "click while overlay up moved focus"
assert session.active() == hover_target
finally:
session.mods(0)
session.wait_for(r"\[mru\] commit app=%d" % hover_target, timeout=10.0)
assert session.active() == hover_target
def test_overlay_pointer_click_swallowed(tmp_path):
"""Clicks while the overlay is up never reach clients: a wltest client
with --pointer-out sees buttons with the overlay down and nothing
new while Alt is held."""
out = tmp_path / "pointer.out"
with _run_kick(
["%s --pointer-out '%s' '#ff0000'" % (WL, out)]) as s:
s.wait_any_active()
s.wait_surface_mapped(0)
path = s.dump_host()
w, h, _ = s.ppm_rgb(path)
s.warp(w // 2, h // 2)
s.click()
assert _wait_out(out, "button 272 1", timeout=10.0), \
f"client saw no click with overlay down: {out.read_text()!r}"
n0 = out.read_text().count("button")
s.mods(ALT)
try:
time.sleep(0.7)
s.click()
time.sleep(0.8)
finally:
s.mods(0)
n1 = out.read_text().count("button")
assert n1 == n0, f"click leaked to client while overlay up: {out.read_text()!r}"
def test_synth_feed_drives_host_listeners(tmp_path):
"""Synthesized host pointer/touch events go through the real host
wl_pointer/wl_touch listeners instead of bypassing them for the shared