test: synth feed drives real host pointer/touch listeners
This commit is contained in:
parent
1aa7665a82
commit
3a194104a3
2 changed files with 200 additions and 6 deletions
125
kick.c
125
kick.c
|
|
@ -8433,6 +8433,39 @@ static bool process_key_event(uint32_t time, uint32_t key, uint32_t state,
|
|||
static void process_modifiers(uint32_t mods_depressed, uint32_t mods_latched,
|
||||
uint32_t mods_locked, uint32_t group);
|
||||
static void host_ptr_motion(uint32_t time, double sx, double sy);
|
||||
/* Host pointer/touch listeners (defined with the host seat handlers). The
|
||||
CTL_CMD_HOST_PTR/HOST_TOUCH feed drives them with NULL objects -- every
|
||||
listener voids its Wayland object -- the same way CTL_CMD_KEY already
|
||||
drives host_kbd_key with NULLs. */
|
||||
static void host_ptr_enter(void *data, struct wl_pointer *p, uint32_t serial,
|
||||
struct wl_surface *surface, wl_fixed_t sx,
|
||||
wl_fixed_t sy);
|
||||
static void host_ptr_leave(void *data, struct wl_pointer *p, uint32_t serial,
|
||||
struct wl_surface *surface);
|
||||
static void host_ptr_motion_event(void *data, struct wl_pointer *p,
|
||||
uint32_t time, wl_fixed_t sx, wl_fixed_t sy);
|
||||
static void host_ptr_button(void *data, struct wl_pointer *p, uint32_t serial,
|
||||
uint32_t time, uint32_t button, uint32_t state);
|
||||
static void host_ptr_axis(void *data, struct wl_pointer *p, uint32_t time,
|
||||
uint32_t axis, wl_fixed_t value);
|
||||
static void host_ptr_axis_source(void *data, struct wl_pointer *p,
|
||||
uint32_t source);
|
||||
static void host_ptr_axis_discrete(void *data, struct wl_pointer *p,
|
||||
uint32_t axis, int32_t discrete);
|
||||
static void host_ptr_axis_stop(void *data, struct wl_pointer *p,
|
||||
uint32_t time, uint32_t axis);
|
||||
static void host_ptr_frame(void *data, struct wl_pointer *p);
|
||||
static void host_touch_down(void *data, struct wl_touch *touch,
|
||||
uint32_t serial, uint32_t time,
|
||||
struct wl_surface *surface, int32_t id,
|
||||
wl_fixed_t fx, wl_fixed_t fy);
|
||||
static void host_touch_motion(void *data, struct wl_touch *touch,
|
||||
uint32_t time, int32_t id,
|
||||
wl_fixed_t fx, wl_fixed_t fy);
|
||||
static void host_touch_up(void *data, struct wl_touch *touch,
|
||||
uint32_t serial, uint32_t time, int32_t id);
|
||||
static void host_touch_cancel(void *data, struct wl_touch *touch);
|
||||
static void host_touch_frame(void *data, struct wl_touch *touch);
|
||||
|
||||
/* Touch delivery shared by native touchscreens, the nested host forwarder
|
||||
and the ctl feed. `action` is a KICK_TOUCH_* value; (ox,oy) are
|
||||
|
|
@ -10035,6 +10068,13 @@ static void handle_new_input(struct wl_listener *listener, void *data) {
|
|||
#define CTL_CMD_SWIPE 7
|
||||
#define CTL_CMD_PINCH 8
|
||||
#define CTL_CMD_HOLD 9
|
||||
/* HOST_PTR/HOST_TOUCH drive the real host wl_pointer/wl_touch listeners with
|
||||
synthesized events (a 17-byte record: sub-command + three u32 args). The
|
||||
listeners void their Wayland objects, the same way CTL_CMD_KEY already
|
||||
drives host_kbd_key with NULLs, so the test feed covers the nested input
|
||||
path instead of bypassing it. */
|
||||
#define CTL_CMD_HOST_PTR 10
|
||||
#define CTL_CMD_HOST_TOUCH 11
|
||||
|
||||
#define CTL_MAX_MSG 32 /* pinch carries six u32s after padding */
|
||||
#define CTL_BUF_SIZE 512
|
||||
|
|
@ -10156,6 +10196,78 @@ static void ctl_dispatch(const uint8_t *msg, size_t len) {
|
|||
case 2: gesture_hold_end(ctl_time, true); break;
|
||||
}
|
||||
break;
|
||||
case CTL_CMD_HOST_PTR:
|
||||
/* Synthesized host pointer event through the real wl_pointer
|
||||
listener (sub, a, b, c). Coordinates are output pixels. */
|
||||
if (len < 17) return;
|
||||
memcpy(u32, msg + 1, 16);
|
||||
ctl_time += 1;
|
||||
switch (u32[0]) {
|
||||
case 0: /* enter: serial, x, y */
|
||||
host_ptr_enter(NULL, NULL, u32[1], NULL,
|
||||
wl_fixed_from_int((int)u32[2]),
|
||||
wl_fixed_from_int((int)u32[3]));
|
||||
break;
|
||||
case 1: /* leave: serial */
|
||||
host_ptr_leave(NULL, NULL, u32[1], NULL);
|
||||
break;
|
||||
case 2: /* motion: x, y */
|
||||
host_ptr_motion_event(NULL, NULL, ctl_time,
|
||||
wl_fixed_from_int((int)u32[1]),
|
||||
wl_fixed_from_int((int)u32[2]));
|
||||
break;
|
||||
case 3: /* button: button, state */
|
||||
host_ptr_button(NULL, NULL, ++ctl_serial, ctl_time,
|
||||
u32[1], u32[2]);
|
||||
break;
|
||||
case 4: /* axis: axis, value (pixels) */
|
||||
host_ptr_axis(NULL, NULL, ctl_time, u32[1],
|
||||
wl_fixed_from_int((int)u32[2]));
|
||||
break;
|
||||
case 5: /* axis_discrete: axis, discrete */
|
||||
host_ptr_axis_discrete(NULL, NULL, u32[1], (int32_t)u32[2]);
|
||||
break;
|
||||
case 6: /* axis_source: source */
|
||||
host_ptr_axis_source(NULL, NULL, u32[1]);
|
||||
break;
|
||||
case 7: /* axis_stop: axis */
|
||||
host_ptr_axis_stop(NULL, NULL, ctl_time, u32[1]);
|
||||
break;
|
||||
case 8: /* frame */
|
||||
host_ptr_frame(NULL, NULL);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case CTL_CMD_HOST_TOUCH:
|
||||
/* Synthesized host touch event through the real wl_touch listener
|
||||
(sub, a, b, c). Coordinates are output pixels. */
|
||||
if (len < 17) return;
|
||||
memcpy(u32, msg + 1, 16);
|
||||
ctl_time += 1;
|
||||
switch (u32[0]) {
|
||||
case 0: /* down: slot, x, y */
|
||||
host_touch_down(NULL, NULL, ++ctl_serial, ctl_time, NULL,
|
||||
(int32_t)u32[1],
|
||||
wl_fixed_from_int((int)u32[2]),
|
||||
wl_fixed_from_int((int)u32[3]));
|
||||
break;
|
||||
case 1: /* motion: slot, x, y */
|
||||
host_touch_motion(NULL, NULL, ctl_time, (int32_t)u32[1],
|
||||
wl_fixed_from_int((int)u32[2]),
|
||||
wl_fixed_from_int((int)u32[3]));
|
||||
break;
|
||||
case 2: /* up: slot */
|
||||
host_touch_up(NULL, NULL, ++ctl_serial, ctl_time,
|
||||
(int32_t)u32[1]);
|
||||
break;
|
||||
case 3: /* cancel */
|
||||
host_touch_cancel(NULL, NULL);
|
||||
break;
|
||||
case 4: /* frame */
|
||||
host_touch_frame(NULL, NULL);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -10183,13 +10295,14 @@ static int ctl_cb(int fd, uint32_t mask, void *data) {
|
|||
|
||||
uint8_t msg[CTL_MAX_MSG];
|
||||
while (have >= 5) {
|
||||
/* MODS/SWIPE carry four u32s, TOUCH three, PINCH six after the
|
||||
command byte; HOLD is a single u32 pair like the rest. */
|
||||
/* MODS/SWIPE/TOUCH/HOST_PTR/HOST_TOUCH carry four u32s, PINCH six
|
||||
after the command byte; HOLD is a single u32 pair like the rest. */
|
||||
size_t need = (buf[0] == CTL_CMD_MODS || buf[0] == CTL_CMD_TOUCH ||
|
||||
buf[0] == CTL_CMD_SWIPE)
|
||||
? 17
|
||||
: buf[0] == CTL_CMD_PINCH ? 25
|
||||
: 9;
|
||||
buf[0] == CTL_CMD_SWIPE || buf[0] == CTL_CMD_HOST_PTR ||
|
||||
buf[0] == CTL_CMD_HOST_TOUCH)
|
||||
? 17
|
||||
: buf[0] == CTL_CMD_PINCH ? 25
|
||||
: 9;
|
||||
if (have < need) break;
|
||||
memcpy(msg, buf, need);
|
||||
ctl_dispatch(msg, need);
|
||||
|
|
|
|||
|
|
@ -126,6 +126,26 @@ KICK_REPEAT_DEFAULT = "600"
|
|||
CTL_CMD_SWIPE = 7
|
||||
CTL_CMD_PINCH = 8
|
||||
CTL_CMD_HOLD = 9
|
||||
CTL_CMD_HOST_PTR = 10
|
||||
CTL_CMD_HOST_TOUCH = 11
|
||||
# Host-pointer sub-commands (sub, a, b, c), mirroring wl_pointer events.
|
||||
HP_ENTER = 0
|
||||
HP_LEAVE = 1
|
||||
HP_MOTION = 2
|
||||
HP_BUTTON = 3
|
||||
HP_AXIS = 4
|
||||
HP_DISCRETE = 5
|
||||
HP_SOURCE = 6
|
||||
HP_STOP = 7
|
||||
HP_FRAME = 8
|
||||
# Host-touch sub-commands, mirroring wl_touch events.
|
||||
HT_DOWN = 0
|
||||
HT_MOTION = 1
|
||||
HT_UP = 2
|
||||
HT_CANCEL = 3
|
||||
HT_FRAME = 4
|
||||
# Evdev button code for BTN_LEFT, as the compositor expects.
|
||||
BTN_LEFT = 0x110
|
||||
# Gesture phases: 0 begin / 1 update / 2 end (3 cancels swipe/pinch;
|
||||
# hold has no update phase, so its phases are renamed below).
|
||||
GESTURE_BEGIN = 0
|
||||
|
|
@ -370,6 +390,21 @@ class Session:
|
|||
self.proc.stdin.write(record)
|
||||
self.proc.stdin.flush()
|
||||
|
||||
def host_ptr(self, sub, a=0, b=0, c=0):
|
||||
"""Synthesized host wl_pointer event through the real host listener
|
||||
(enter/motion/button/axis/...), the same object a live host seat
|
||||
would deliver."""
|
||||
record = struct.pack("<BIIII", CTL_CMD_HOST_PTR, sub, a, b, c)
|
||||
self.proc.stdin.write(record)
|
||||
self.proc.stdin.flush()
|
||||
|
||||
def host_touch(self, sub, a=0, b=0, c=0):
|
||||
"""Synthesized host wl_touch event through the real host listener
|
||||
(down/motion/up/cancel/frame)."""
|
||||
record = struct.pack("<BIIII", CTL_CMD_HOST_TOUCH, sub, a, b, c)
|
||||
self.proc.stdin.write(record)
|
||||
self.proc.stdin.flush()
|
||||
|
||||
def pointer_enter_until(self, pattern, timeout=20.0):
|
||||
"""Keep re-entering the surface until `pattern` matches the log.
|
||||
|
||||
|
|
@ -4323,6 +4358,52 @@ def test_touch_delivery_to_client():
|
|||
assert "motion" in content
|
||||
|
||||
|
||||
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
|
||||
inners: enter/motion/button/axis/discrete/source/stop/frame/leave plus
|
||||
touch down/motion/up/cancel/frame all execute, touch still delivers to
|
||||
the client, and the session survives the burst. Guards the nested input
|
||||
path the stdin feed would otherwise never cover."""
|
||||
logf = tmp_path / "touch.log"
|
||||
with _run_standalone(
|
||||
{}, f"{WL} --touch-log {logf} '#306090'",
|
||||
wait="app0_mapped") as s:
|
||||
s.host_ptr(HP_ENTER, 1, 300, 300)
|
||||
s.host_ptr(HP_MOTION, 320, 320)
|
||||
s.host_ptr(HP_BUTTON, BTN_LEFT, 1)
|
||||
s.host_ptr(HP_BUTTON, BTN_LEFT, 0)
|
||||
s.host_ptr(HP_DISCRETE, 0, 1)
|
||||
s.host_ptr(HP_SOURCE, 0)
|
||||
s.host_ptr(HP_AXIS, 0, 15)
|
||||
s.host_ptr(HP_STOP, 0)
|
||||
s.host_ptr(HP_FRAME)
|
||||
s.host_ptr(HP_LEAVE, 1)
|
||||
_assert_alive(s, "compositor died on host pointer burst")
|
||||
s.host_touch(HT_DOWN, 7, 300, 300)
|
||||
s.host_touch(HT_MOTION, 7, 340, 340)
|
||||
s.host_touch(HT_UP, 7)
|
||||
deadline = time.monotonic() + 5
|
||||
content = ""
|
||||
while time.monotonic() < deadline:
|
||||
if os.path.exists(logf):
|
||||
content = open(logf).read()
|
||||
if "down" in content and "up" in content:
|
||||
break
|
||||
time.sleep(0.2)
|
||||
assert "down" in content and "up" in content, \
|
||||
f"host-path touch never delivered: {content!r}"
|
||||
dline = [l for l in content.splitlines()
|
||||
if l.startswith("down")][0]
|
||||
assert " 7 " in f" {dline} ", f"wrong slot id: {dline}"
|
||||
# A second sequence ended by cancel (not up) still ends cleanly.
|
||||
s.host_touch(HT_DOWN, 8, 300, 300)
|
||||
s.host_touch(HT_CANCEL)
|
||||
s.host_touch(HT_FRAME)
|
||||
time.sleep(0.5)
|
||||
_assert_alive(s, "compositor died on host touch cancel")
|
||||
|
||||
|
||||
def test_alt_shift_move_no_member_is_noop():
|
||||
"""Shift+Left/Right with the prefix held on a tab whose focused member
|
||||
is the main surface still gets consumed (never leaks Shift+arrow to
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue