fix: the plot's two side margins are balanced, and the dump outlives its widgets
With the axis titles gone the left gutter was 200 px of an 870 px viewport and the right side had no margin at all, so the newest tick's label was drawn half outside the plot and the newest sample's ink landed on the frame. Two causes on the left. The reservation covered `format_si()`'s `%.3g` fallback (`-1.23e-45`), which no signal on an `Si` axis produces -- the widest real label for a unitless axis is `-1.23m` -- and `note_y_gutter()` added 6 px of slack to a figure that is already the maximum of the labels and last frame's floor, making the gutter a ratchet that climbed to its 200 px cap in ~19 frames. It settles at 104 px now, with `axes.w changes: 1` where it read 17. The right side did not exist: a plot's `axes` rect reached the viewport's right edge by construction. `deps/plotter` gains `Plot::set_right_gutter()` and `Figure::set_right_gutter()` -- a want rather than a measurement, since nothing is drawn there, imposed figure-wide so the rows end on one column as they already start on one. udpscope asks for half the widest time label plus the label padding (59 px), x labels being centred on their tick. Separately: the exit dump asked two destroyed widgets for their size, which is where the "exactly two Gtk-CRITICALs" baseline came from. The pointer is freed memory by then, so a run that had destroyed other windows first (UDPSCOPE_PROBE_HELP_MS opens three) segfaulted in the middle of the dump instead. Both sizes are captured on the tick; a clean run now carries no critical. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
811c6d80f0
commit
62df049f53
7 files changed
+153
-39
No files matched your search
@@ -41,6 +41,15 @@ both states, the frame and per-channel sample counts, each plot's painted-frame
|
||||
count, which channel each trace slot holds, and the allocated size and reveal
|
||||
state of both side panels.
|
||||
|
||||
Every size it prints is read from a value the frame tick captured while the window was alive, not
|
||||
from the widget: the dump runs from the destroy handler, where the widget pointer is already freed
|
||||
memory. It used to ask two of them directly, which is where the long-standing "an exit-dump run
|
||||
emits exactly two `Gtk-CRITICAL`s" baseline came from — and a critical was only the lucky reading.
|
||||
`UDPSCOPE_PROBE_HELP_MS` builds and destroys three windows of its own before the dump, so by then
|
||||
the memory had been reused and the same two calls **crashed in the middle of the dump**, which
|
||||
looks like a truncated log rather than like a segfault in a debug line. The dump now makes no
|
||||
`gtk_widget_*` call at all and a clean run carries no critical.
|
||||
|
||||
## Layout
|
||||
|
||||
```
|
||||
@@ -1083,13 +1092,40 @@ which is how the second one was found:
|
||||
what its labels need, so a floor written onto the rows would survive exactly
|
||||
until the next layout. The figure seeds its common gutter — the maximum over
|
||||
the rows, which is also what aligns their axes — with that reservation.
|
||||
The reservation is the widest label `format_si()` writes for an axis with no
|
||||
unit (a sign, three significant figures and a one-glyph prefix), not the
|
||||
wider `%.3g` fallback it used to cover: that form needs a value past 1e24, so
|
||||
it cost a third of the gutter for a label no real signal produces — and since
|
||||
the floor is a monotone maximum over the *measured* need, one that did appear
|
||||
would widen the gutter for good anyway.
|
||||
2. *The status line drove the pane width.* A `GtkLabel`'s minimum width is its
|
||||
text, and the status line is rewritten every frame; as its counters and
|
||||
messages changed width they moved the paned divider, which resized the plot,
|
||||
which re-laid out the axes and their ticks. The label is now ellipsized, so its
|
||||
minimum is “…”; the plot keeps the width it was given.
|
||||
3. *The gutter was a ratchet.* `note_y_gutter()` is handed the gutter the row was
|
||||
**laid out with** — already the maximum of its labels and the floor imposed on
|
||||
the frame before — and it added six pixels of slack to it. Those six pixels came
|
||||
back next frame and were added again, so the left margin climbed a decade of
|
||||
pixels a second until it hit the 200 px cap and stopped: 200 px of an 870 px
|
||||
viewport, with `axes.w changes: 17` beside it, which reads as a settle and is a
|
||||
runaway that ran out of room. The reservation's own padding is the slack, once;
|
||||
nothing is added to a measurement that is partly our own answer.
|
||||
|
||||
The self-check reports both. `viewport.w` must collapse to a single value, and
|
||||
**The two sides are balanced.** The axes rect used to reach the viewport's right
|
||||
edge, because that is all the library's layout had ever reserved — so the
|
||||
rightmost x tick label, *centred* on its tick, had half of itself outside the plot
|
||||
(a live axis is `[-window, 0]`, so there is always a tick exactly on that edge) and
|
||||
the newest sample's ink landed on the frame. `Plot::set_right_gutter()` and
|
||||
`Figure::set_right_gutter()` were added upstream for this: an empty band, served
|
||||
from the same half-viewport allowance as the label and title strips, set once on
|
||||
every row because the rows' axes have to end on a common column as well as start on
|
||||
one. `RowSet` asks for half the widest time label plus the label padding, which is
|
||||
exactly what the last label needs to be drawn whole. With the two fixes above the
|
||||
figure now sits in a 104 px left gutter holding the y labels and a 59 px empty band
|
||||
on the right, where it was 200 px and nothing.
|
||||
|
||||
The self-check reports all of it. `viewport.w` must collapse to a single value, and
|
||||
`axes.x`, `axes.w` and `y-gutter` must settle: the one-shot label reservation
|
||||
lands a few frames after the first measured frame, and because the figure serves
|
||||
its floor out of the label strip the reservation converges over two layouts, so
|
||||
@@ -1100,9 +1136,9 @@ bug. `x-ticks` is content, and varies by one as a live window slides over a roun
|
||||
number:
|
||||
|
||||
```
|
||||
layout over 952 frames: axes.x 67..80, axes.w 546..559, viewport.w 627..627,
|
||||
y-gutter 44..74, x-ticks 20..21, x-desync 0
|
||||
axes.w changes: 2 (last at frame 35, side panel up=1)
|
||||
layout over 583 frames: axes.x 88..104, axes.w 707..782, viewport.w 870..870,
|
||||
y-gutter 88..104, x-ticks 20..41, x-desync 0
|
||||
axes.w changes: 1 (last at frame 1, side panel up=1)
|
||||
```
|
||||
|
||||
Set `UDPSCOPE_PROBE_PAUSE_MS=<ms>` to pause the acquisition part-way through a
|
||||
@@ -1430,7 +1466,7 @@ them all with nothing to propagate by hand. The live follow is the *group's*
|
||||
(`set_x_link_follow`), not each row's — `Figure::link_x()` refuses a member whose
|
||||
own axis still follows, because a following axis would reinterpret the pushed
|
||||
range as its own span and offset behind its own edge, which is per-row drift by
|
||||
construction. The figure also owns the left gutter and the crosshair, so a
|
||||
construction. The figure also owns both gutters and the crosshair, so a
|
||||
vertical feature lands in the same pixel column in every row.
|
||||
|
||||
The self-check's layout line carries `x-desync N`, the number of frames in which
|
||||
|
||||
Reference in new issue
Block a user