feat: drag across overlay retargets touch selection
This commit is contained in:
parent
0d65efdd49
commit
d91cc8ba8d
2 changed files with 105 additions and 11 deletions
51
kick.c
51
kick.c
|
|
@ -11097,6 +11097,10 @@ static size_t touch_slot_count;
|
|||
motion/up must not leak to clients as a partial gesture. */
|
||||
static int32_t overlay_touch_slots[KICK_TOUCH_MAX_SLOTS];
|
||||
static size_t overlay_touch_count;
|
||||
/* First touch id that went down while the overlay was up: the only finger
|
||||
whose motion re-targets the preview (first-touch-wins). -1 when no
|
||||
overlay drag is active. */
|
||||
static int32_t overlay_touch_driver = -1;
|
||||
|
||||
static bool overlay_touch_consumed(int32_t id) {
|
||||
for (size_t i = 0; i < overlay_touch_count; i++)
|
||||
|
|
@ -11114,16 +11118,21 @@ static void overlay_touch_release(int32_t id) {
|
|||
for (size_t i = 0; i < overlay_touch_count; i++)
|
||||
if (overlay_touch_slots[i] == id) {
|
||||
overlay_touch_slots[i] = overlay_touch_slots[--overlay_touch_count];
|
||||
return;
|
||||
break;
|
||||
}
|
||||
if (id == overlay_touch_driver)
|
||||
overlay_touch_driver = -1;
|
||||
}
|
||||
|
||||
/* A tap on an overlay chip while the prefix is held: preview that window
|
||||
(the release commits it to the MRU front via the usual overlay path).
|
||||
Mirrors mru_step's set_active_tab + window_enter pinning so a stacked
|
||||
member is selected exactly, not just its tab's current window. */
|
||||
member is selected exactly, not just its tab's current window. Re-taps
|
||||
(or drag motion) landing on the already-shown window are a no-op, so
|
||||
motion-rate events never spam the log. */
|
||||
static void overlay_touch_select(struct kick_window *w) {
|
||||
if (!w || server.session_locked) return;
|
||||
if (w == mru_current()) return;
|
||||
struct kick_tab *tab = w->tab;
|
||||
if (!tab || !tab_overlay_row(tab)) return;
|
||||
if (tab != server.active_tab)
|
||||
|
|
@ -11138,22 +11147,41 @@ static void seat_touch_deliver(uint32_t time_msec, int32_t id, int action,
|
|||
if (server.idle_notifier)
|
||||
wlr_idle_notifier_v1_notify_activity(server.idle_notifier, server.seat);
|
||||
/* While the Alt overlay is up, touches never reach clients: a tap on
|
||||
a row previews that window (single tap selects), anything else is
|
||||
ignored. Checked before constraint clamping -- the overlay sits
|
||||
outside any constrained surface. */
|
||||
a row previews that window (single tap selects, drag motion
|
||||
re-targets the preview as the finger slides between titles),
|
||||
anything else is ignored. Checked before constraint clamping --
|
||||
the overlay sits outside any constrained surface. */
|
||||
if (host.overlay_visible && !confirm_quit_pending && !server.session_locked) {
|
||||
if (action == KICK_TOUCH_DOWN) {
|
||||
struct kick_tab *ht = NULL;
|
||||
struct kick_window *hw = NULL;
|
||||
if (overlay_hit_test(ox, oy, &ht, &hw) && hw)
|
||||
overlay_touch_select(hw);
|
||||
overlay_touch_consume(id);
|
||||
/* First-touch-wins: only the finger that claims the idle
|
||||
driver previews; concurrent fingers are swallowed silently
|
||||
(and never promote when the driver lifts, to avoid jumps). */
|
||||
if (overlay_touch_driver == -1) {
|
||||
struct kick_tab *ht = NULL;
|
||||
struct kick_window *hw = NULL;
|
||||
if (overlay_hit_test(ox, oy, &ht, &hw) && hw)
|
||||
overlay_touch_select(hw);
|
||||
overlay_touch_consume(id);
|
||||
overlay_touch_driver = id;
|
||||
} else {
|
||||
overlay_touch_consume(id);
|
||||
}
|
||||
wlr_seat_touch_notify_frame(server.seat);
|
||||
return;
|
||||
}
|
||||
if (overlay_touch_consumed(id)) {
|
||||
if (action != KICK_TOUCH_MOTION)
|
||||
if (action == KICK_TOUCH_MOTION) {
|
||||
/* Drag: sliding into another row's title previews it;
|
||||
sliding out of every row sticks to the last preview. */
|
||||
if (id == overlay_touch_driver) {
|
||||
struct kick_tab *ht = NULL;
|
||||
struct kick_window *hw = NULL;
|
||||
if (overlay_hit_test(ox, oy, &ht, &hw) && hw)
|
||||
overlay_touch_select(hw);
|
||||
}
|
||||
} else {
|
||||
overlay_touch_release(id);
|
||||
}
|
||||
wlr_seat_touch_notify_frame(server.seat);
|
||||
return;
|
||||
}
|
||||
|
|
@ -11203,6 +11231,7 @@ static void seat_touch_deliver(uint32_t time_msec, int32_t id, int action,
|
|||
static void seat_touch_cancel_all(void) {
|
||||
/* Overlay-swallowed touches were never forwarded: just drop them. */
|
||||
overlay_touch_count = 0;
|
||||
overlay_touch_driver = -1;
|
||||
while (touch_slot_count > 0)
|
||||
seat_touch_deliver(0, touch_slots[touch_slot_count - 1],
|
||||
KICK_TOUCH_UP, 0, 0);
|
||||
|
|
|
|||
|
|
@ -4897,6 +4897,71 @@ def test_overlay_touch_select(session):
|
|||
assert session.active() == target
|
||||
|
||||
|
||||
def test_overlay_touch_drag(session):
|
||||
"""Dragging a finger across the overlay while Alt is held re-targets
|
||||
the preview as the touch slides into each row's titles; sliding out
|
||||
of every row sticks to the last preview; a second finger never steals
|
||||
the drag; lifting commits nothing (Alt release still 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 tapping 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"\[touch\] select app=", session.read_log()))
|
||||
session.touch(7, TOUCH_DOWN, cx, y)
|
||||
time.sleep(0.05)
|
||||
session.touch(7, TOUCH_UP, cx, y)
|
||||
time.sleep(0.12)
|
||||
m = re.findall(r"\[touch\] 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"touch sweep found rows {rows}"
|
||||
yA, yB = sorted(rows.values())
|
||||
# Drag from the upper row into the lower one step by step.
|
||||
base = len(re.findall(r"\[touch\] select app=", session.read_log()))
|
||||
session.touch(7, TOUCH_DOWN, cx, yA)
|
||||
time.sleep(0.2)
|
||||
for yy in range(yA, yB + 1, 10):
|
||||
session.touch(7, TOUCH_MOTION, cx, yy)
|
||||
time.sleep(0.03)
|
||||
time.sleep(0.3)
|
||||
sels = re.findall(r"\[touch\] select app=(\d+)", session.read_log())
|
||||
assert len(sels) > base and sels[base] != sels[-1], \
|
||||
f"drag never re-targeted: {sels[base:]}"
|
||||
drag_target = int(sels[-1])
|
||||
# Sliding out of every row keeps the last preview.
|
||||
n0 = len(re.findall(r"\[touch\] select app=", session.read_log()))
|
||||
session.touch(7, TOUCH_MOTION, 5, 5)
|
||||
time.sleep(0.3)
|
||||
n1 = len(re.findall(r"\[touch\] select app=", session.read_log()))
|
||||
assert n1 == n0, "off-row drag motion changed selection"
|
||||
# A second finger's down+motion never steals the drag.
|
||||
session.touch(9, TOUCH_DOWN, cx, yA)
|
||||
time.sleep(0.1)
|
||||
session.touch(9, TOUCH_MOTION, cx, yB)
|
||||
time.sleep(0.2)
|
||||
n2 = len(re.findall(r"\[touch\] select app=", session.read_log()))
|
||||
session.touch(9, TOUCH_UP, cx, yB)
|
||||
time.sleep(0.1)
|
||||
assert n2 == n1, "second finger stole the overlay drag"
|
||||
session.touch(7, TOUCH_UP, cx, yB)
|
||||
time.sleep(0.2)
|
||||
assert session.active() == drag_target
|
||||
finally:
|
||||
session.mods(0)
|
||||
session.wait_for(r"\[mru\] commit app=%d" % drag_target, timeout=10.0)
|
||||
assert session.active() == drag_target
|
||||
|
||||
|
||||
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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue