test(scale): add automated tests, manual QA checklist, and build integration

- Automated scale tests: float tolerance, safety guard, ceilf fallback
- Manual QA checklist: 8 items covering fractional, mixed-DPI, XWayland
- Build: test_scale Makefile target (standalone C23, no wlroots deps)
This commit is contained in:
2026-07-05 21:53:30 -04:00
parent dac0b4fa6f
commit 48a4aed0ec
5 changed files with 492 additions and 2 deletions
@@ -0,0 +1,115 @@
================================================================================
EVIDENCE: Task 5 — wlr_fractional_scale_manager_v1 propagation chain
================================================================================
Goal: Verify that scale-change notifications reach surfaces when any output
configuration changes (wlr-randr, hotplug, layout rejig, etc.).
================================================================================
PATH 1 (direct): outputmgrapplyortest() → updatemons()
================================================================================
File: src/peachwm.c
Function: outputmgrapplyortest() (line 2553)
Call site: line 2612 — updatemons(nullptr, nullptr);
Triggered by:
- outputmgrapply() (line 2548 — from wlr_output_manager config apply)
- outputmgrtest() (line 2615 — from wlr_output_manager test)
This path fires when a client (e.g. wlr-randr) sends a new output
configuration. After committing the per-head state and sending the
success/failure response, updatemons() is called unconditionally.
CONFIRMED ✅ — updatemons(nullptr, nullptr) at line 2612.
================================================================================
PATH 2 (indirect): output_layout.change signal → updatemons()
================================================================================
Listener declaration: line 364
static struct wl_listener layout_change = {.notify = updatemons};
Signal wiring: line 3691
wl_signal_add(&output_layout->events.change, &layout_change);
This path fires when the output layout changes via any other route
(e.g. hotplug, wlr_output_layout_add call inside updatemons itself).
It is the "belt" to the "suspenders" of Path 1.
Note: outputmgrapplyortest() calls wlr_output_layout_add() at line 2600,
which itself triggers output_layout.change — so Path 2 also fires
during Path 1. updatemons() is idempotent (no ill effect from
running twice in a single event cycle).
CONFIRMED ✅ — layout_change.notify = updatemons at line 364,
wired at line 3691.
================================================================================
SCALE RE-NOTIFICATION HOOK (inside updatemons)
================================================================================
File: src/peachwm.c
Function: updatemons() (line 4101)
Hook: lines 4168–4201
For each enabled monitor:
[clients loop, line 4170]
wl_list_for_each(c, &clients, link) {
if (c->mon != m)
continue;
client_update_scale(c); // ← task 4 hook
if (!client_is_x11(c) && c->surface.xdg) {
struct wlr_xdg_popup *popup;
wl_list_for_each(popup, &c->surface.xdg->popups, link)
wlr_fractional_scale_v1_notify_scale(
popup->base->surface, (double)m->wlr_output->scale);
}
}
[layers loop, line 4181]
for (int i = 0; i < 4; i++) {
LayerSurface *l;
wl_list_for_each(l, &m->layers[i], link) {
layersurface_update_scale(l); // ← task 4 hook
{
struct wlr_scene_node *popup_node;
wl_list_for_each(popup_node, &l->popups->children, link) {
if (popup_node->type != WLR_SCENE_NODE_BUFFER)
continue;
struct wlr_scene_buffer *sb =
wlr_scene_buffer_from_node(popup_node);
struct wlr_scene_surface *ss =
wlr_scene_surface_try_from_buffer(sb);
if (ss && ss->surface)
wlr_fractional_scale_v1_notify_scale(
ss->surface, (double)m->wlr_output->scale);
}
}
}
}
This covers both regular xdg-toplevel clients and layer-shell surfaces
(bars, backgrounds, OSDs), including their XDG popups.
CONFIRMED ✅ — scale re-notification hook present and complete.
================================================================================
VERDICT: PROPAGATION CHAIN CONFIRMED ✅
================================================================================
outputmgrapplyortest() → updatemons() [direct: line 2612]
layout_change signal → updatemons() [indirect: lines 364, 3691]
updatemons() → client_update_scale(), layersurface_update_scale(),
wlr_fractional_scale_v1_notify_scale() [lines 4168–4201]
No code changes required. The chain is complete. Scale-change notifications
will reach every surface (clients + layers + popups) whenever output
configuration changes through ANY path.
Test: wlr-randr --output DP-1 --scale 2 # triggers Path 1 via config apply
# which calls updatemons, which re-notifies every surface on that output.
# Also triggers Path 2 via wlr_output_layout_add calling events.change.
================================================================================