wayland: Phase 13 — pointer focus delivery to Wayland surfaces
Implement view_surface_at() and wl_update_pointer_focus() to deliver wl_pointer.enter/motion/frame events to the Wayland surface under the virtual pointer. wl_client_synth_configure now records the WM-assigned position in view->x/y so hit-testing works correctly. After wManageWindow succeeds, the virtual pointer is warped to the window centre to immediately deliver pointer focus. Smoke test check 16c verifies "pointer focus entered surface" is logged. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
5c85016a9b
commit
fae9360af5
|
|
@ -201,6 +201,9 @@ frame_timer_cb(void *data)
|
|||
static void wl_focus_set(WNativeWindow win, int revert_to, unsigned long timestamp);
|
||||
/* Phase 50: forward declaration — defined in client section below. */
|
||||
static int wl_client_get_pid(WNativeWindow win);
|
||||
/* Phase 13: forward declarations — defined in pointer section below. */
|
||||
static void wl_update_pointer_focus(uint32_t time_msec);
|
||||
static void wl_pointer_warp_to(int x, int y);
|
||||
|
||||
/* Phase 41: look up the view for a given synthetic WNativeWindow ID. */
|
||||
static struct wl_toplevel_view *
|
||||
|
|
@ -285,6 +288,16 @@ handle_toplevel_map(struct wl_listener *listener, void *data)
|
|||
/* Phase 62: focus delivered by wManageWindow via wSetFocusTo
|
||||
* (auto_focus=1 set in wl_screen_create_internals); the
|
||||
* direct wl_focus_set() call is no longer needed here. */
|
||||
|
||||
/* Phase 13: warp virtual pointer to the centre of the
|
||||
* newly managed window so pointer focus is immediately
|
||||
* delivered to its surface. The warp calls
|
||||
* wl_update_pointer_focus() internally. */
|
||||
{
|
||||
int cx = view->x + wwin->client.width / 2;
|
||||
int cy = view->y + wwin->client.height / 2;
|
||||
wl_pointer_warp_to(cx, cy);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -853,6 +866,74 @@ wl_pointer_grab(WNativeWindow grab_win, WNativeWindow confine_to,
|
|||
|
||||
static void wl_pointer_ungrab(unsigned long timestamp) { (void)timestamp; }
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Phase 13: pointer focus delivery to Wayland surfaces */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
/*
|
||||
* Find the topmost mapped view whose surface contains screen point (px, py).
|
||||
* Returns the wlr_surface and sets *sx/*sy to surface-local coordinates.
|
||||
* Iterates toplevel_list in reverse-insertion order (last = topmost).
|
||||
*/
|
||||
static struct wlr_surface *
|
||||
view_surface_at(int px, int py, double *sx_out, double *sy_out,
|
||||
struct wl_toplevel_view **view_out)
|
||||
{
|
||||
struct wl_toplevel_view *v;
|
||||
wl_list_for_each_reverse(v, &wl_state.toplevel_list, link) {
|
||||
if (!v->xdg_surface || !v->xdg_surface->mapped)
|
||||
continue;
|
||||
double sx = px - (double)v->x;
|
||||
double sy = py - (double)v->y;
|
||||
double sub_sx, sub_sy;
|
||||
struct wlr_surface *sub = wlr_xdg_surface_surface_at(
|
||||
v->xdg_surface, sx, sy, &sub_sx, &sub_sy);
|
||||
if (sub) {
|
||||
if (sx_out) *sx_out = sub_sx;
|
||||
if (sy_out) *sy_out = sub_sy;
|
||||
if (view_out) *view_out = v;
|
||||
return sub;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Update the seat's pointer focus to the surface at the current virtual
|
||||
* pointer position. Called after every pointer position change so clients
|
||||
* receive wl_pointer.enter / wl_pointer.leave events correctly.
|
||||
*/
|
||||
static void
|
||||
wl_update_pointer_focus(uint32_t time_msec)
|
||||
{
|
||||
if (!wl_state.seat)
|
||||
return;
|
||||
|
||||
double sx, sy;
|
||||
struct wl_toplevel_view *view = NULL;
|
||||
struct wlr_surface *surface = view_surface_at(
|
||||
wl_state.pointer_x, wl_state.pointer_y, &sx, &sy, &view);
|
||||
|
||||
static struct wlr_surface *last_focused_surface = NULL;
|
||||
if (surface) {
|
||||
if (surface != last_focused_surface) {
|
||||
wwarning("wl_backend: pointer focus entered surface %p at %.1f,%.1f",
|
||||
(void *)surface, sx, sy);
|
||||
last_focused_surface = surface;
|
||||
}
|
||||
wlr_seat_pointer_notify_enter(wl_state.seat, surface, sx, sy);
|
||||
wlr_seat_pointer_notify_motion(wl_state.seat, time_msec, sx, sy);
|
||||
wlr_seat_pointer_notify_frame(wl_state.seat);
|
||||
} else {
|
||||
if (last_focused_surface) {
|
||||
wwarning("wl_backend: pointer left all surfaces");
|
||||
last_focused_surface = NULL;
|
||||
}
|
||||
wlr_seat_pointer_clear_focus(wl_state.seat);
|
||||
}
|
||||
(void)view; /* future: WM hit-testing / raise-on-hover */
|
||||
}
|
||||
|
||||
static void
|
||||
wl_pointer_warp(int dx, int dy)
|
||||
{
|
||||
|
|
@ -863,6 +944,8 @@ wl_pointer_warp(int dx, int dy)
|
|||
if (wl_state.pointer_y < 0) wl_state.pointer_y = 0;
|
||||
if (wl_state.pointer_x > 1919) wl_state.pointer_x = 1919;
|
||||
if (wl_state.pointer_y > 1079) wl_state.pointer_y = 1079;
|
||||
/* Phase 13: update pointer focus after relative warp */
|
||||
wl_update_pointer_focus(0);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -875,6 +958,8 @@ wl_pointer_warp_to(int x, int y)
|
|||
if (y > 1079) y = 1079;
|
||||
wl_state.pointer_x = x;
|
||||
wl_state.pointer_y = y;
|
||||
/* Phase 13: update pointer focus after absolute warp */
|
||||
wl_update_pointer_focus(0);
|
||||
}
|
||||
|
||||
static int
|
||||
|
|
@ -1248,11 +1333,15 @@ wl_client_synth_configure(WNativeWindow client_win,
|
|||
int x, int y, int width, int height,
|
||||
int border_width, WNativeWindow above)
|
||||
{
|
||||
/* Phase 44: send an XDG configure with the new size to the client. */
|
||||
(void)x; (void)y; (void)border_width; (void)above;
|
||||
/* Phase 44: send an XDG configure with the new size to the client.
|
||||
* Phase 13: also record the WM-assigned position in the view so
|
||||
* pointer hit-testing (view_surface_at) uses current coordinates. */
|
||||
(void)border_width; (void)above;
|
||||
struct wl_toplevel_view *v = wl_find_view_by_id(client_win);
|
||||
if (!v || !v->xdg_surface)
|
||||
return;
|
||||
v->x = x;
|
||||
v->y = y;
|
||||
if (width > 0 && height > 0)
|
||||
wlr_xdg_toplevel_set_size(v->xdg_surface, (uint32_t)width, (uint32_t)height);
|
||||
wlr_xdg_surface_schedule_configure(v->xdg_surface);
|
||||
|
|
|
|||
|
|
@ -104,7 +104,13 @@
|
|||
# from the headless keyboard are dispatched through the WM shortcut
|
||||
# handler before being forwarded to focused clients.
|
||||
# Verified by "keyboard key+modifiers listeners wired" log.
|
||||
# 37. (Phase 11) noclip/nodock set in wl_screen_create_internals: wDockCreate
|
||||
# 37. (Phase 13) Pointer focus delivered: after wManageWindow succeeds the
|
||||
# virtual pointer is warped to the window centre; wl_update_pointer_focus
|
||||
# calls wlr_seat_pointer_notify_enter/motion/frame so the Wayland client
|
||||
# receives wl_pointer.enter events. wl_client_synth_configure now records
|
||||
# the WM-assigned position (v->x/v->y) so view_surface_at() returns the
|
||||
# correct surface. Verified by "pointer focus entered surface" log.
|
||||
# 38. (Phase 11) noclip/nodock set in wl_screen_create_internals: wDockCreate
|
||||
# requires X11 rendering resources unavailable in Wayland mode; disabling
|
||||
# the dock/clip prevents crashes when wWorkspaceNew is called to create
|
||||
# additional workspaces. App icon creation path (wApplicationCreate →
|
||||
|
|
@ -332,6 +338,16 @@ if command -v weston-simple-shm >/dev/null 2>&1; then
|
|||
|| die "compositor did not log 'focus_clear' (wl_focus_set WNATIVE_NONE path not reached)"
|
||||
echo " focus_clear check: keyboard focus cleared after last window unmap"
|
||||
|
||||
# -- 16c. Phase 13: pointer focus delivered to mapped surface ------------
|
||||
#
|
||||
# After wManageWindow succeeds, wl_backend warps the virtual pointer to
|
||||
# the centre of the newly managed window and calls wl_update_pointer_focus
|
||||
# which sends wl_pointer.enter to the surface. The compositor logs
|
||||
# "pointer focus entered surface" when this happens.
|
||||
grep -q 'pointer focus entered surface' /tmp/wl-smoke-stderr \
|
||||
|| die "compositor did not deliver pointer focus to any managed surface"
|
||||
echo " ptr_focus check: pointer focus delivered to managed surface"
|
||||
|
||||
# -- 21. Phase 63: DELETE_WINDOW=1 → kill_close=0 for Wayland toplevels --
|
||||
#
|
||||
# PropGetProtocols now sets DELETE_WINDOW=1 for all Wayland windows so
|
||||
|
|
|
|||
Loading…
Reference in New Issue