wayland: Phase 45 — pointer_query returns on-screen; focus path guarded

pointer_query / pointer_query_full: return 1 when compositor is running
(wl_state.started) so the same_screen check in wManageWindow passes and
wSetFocusTo is called for newly mapped XDG toplevels.

Guard frame-dependent calls in the focus update path:
  - wWindowFocus (window.c): guard wFrameWindowChangeState on frame!=NULL
  - wWindowUnfocus (window.c): same; also guard wFrameWindowPaint on
    owner->frame when clearing semi-focus on transient owner

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Levi Neely 2026-03-28 06:43:16 +01:00
parent 4574fe8873
commit 69cee6fb15
3 changed files with 14 additions and 5 deletions

View File

@ -25,6 +25,8 @@
* device created and attached to seat; mapped toplevels auto-focused.
* Phase 44: client_synth_configure, window_resize send XDG configure events
* so clients receive size changes from the WM.
* Phase 45: pointer_query returns 1 (always on-screen); wWindowFocus/Unfocus
* guarded on frame!=NULL so wSetFocusTo works without a frame window.
*/
#ifdef HAVE_CONFIG_H
@ -610,9 +612,10 @@ static void wl_pointer_warp_to(int x, int y) { (void)x; (void)y; }
static int
wl_pointer_query(WNativeWindow root, int *root_x, int *root_y)
{
/* Phase 45: the Wayland compositor IS the screen; always on-screen. */
(void)root;
*root_x = *root_y = 0;
return 0;
return wl_state.started ? 1 : 0;
}
static int
@ -621,11 +624,12 @@ wl_pointer_query_full(WNativeWindow root,
int *root_x, int *root_y,
unsigned int *buttons_out)
{
/* Phase 45: same — always on-screen when compositor is running. */
(void)root;
if (child_out) *child_out = WNATIVE_NONE;
*root_x = *root_y = 0;
if (buttons_out) *buttons_out = 0;
return 0;
return wl_state.started ? 1 : 0;
}
static void

View File

@ -1729,7 +1729,7 @@ void wWindowFocus(WWindow *wwin, WWindow *owin)
wwin->flags.semi_focused = 0;
if (wwin->flags.is_gnustep == 0)
if (wwin->flags.is_gnustep == 0 && wwin->frame)
wFrameWindowChangeState(wwin->frame, WS_FOCUSED);
wwin->flags.focused = 1;
@ -1783,7 +1783,7 @@ void wWindowUnfocus(WWindow *wwin)
{
CloseWindowMenu(wwin->screen_ptr);
if (wwin->flags.is_gnustep == 0)
if (wwin->flags.is_gnustep == 0 && wwin->frame)
wFrameWindowChangeState(wwin->frame, wwin->flags.semi_focused ? WS_PFOCUSED : WS_UNFOCUSED);
if (wwin->transient_for != None && wwin->transient_for != wwin->screen_ptr->root_win) {
@ -1793,7 +1793,8 @@ void wWindowUnfocus(WWindow *wwin)
owner->flags.semi_focused = 0;
if (owner->flags.mapped || owner->flags.shaded) {
wWindowUnfocus(owner);
wFrameWindowPaint(owner->frame);
if (owner->frame)
wFrameWindowPaint(owner->frame);
}
}
}

View File

@ -28,6 +28,10 @@
# "wManageWindow succeeded" log includes geo= with non-zero size
# 13. (Phase 43) focus_set delivers keyboard focus via wlr_seat: compositor
# logs "focus_set id=" after wManageWindow succeeds
# 14. (Phase 44) client_synth_configure/window_resize send XDG configure
# events so clients receive WM-initiated size changes (no external check)
# 15. (Phase 45) pointer_query returns 1 so wSetFocusTo fires from
# wManageWindow; wWindowFocus/Unfocus guarded on frame!=NULL
#
# This test grows as wl_backend vtable slots are implemented.
#