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:
Martino FerrariandClaude Opus 4.6 committed 2026-10-05 22:34:10 +02:00
1 parent 811c6d80f0
commit 62df049f53
7 files changed
+153 -39

No files matched your search

+14 -3
View File
@@ -274,6 +274,13 @@ struct App {
int dbg_win_btn_w = -1;
int dbg_win_label_w = -1;
int dbg_bar_w = -1;
/// The measurements pane's allocation, for the same reason and more urgently: the dump used
/// to ask the widget itself, which is where the two documented `Gtk-CRITICAL`s came from --
/// and a critical is the lucky outcome. The pointer is freed memory by then, so a run that
/// built and destroyed other windows first (`UDPSCOPE_PROBE_HELP_MS`, three of them) has had
/// that memory reused and the read is a **SIGSEGV** in the middle of the dump.
int dbg_measures_pane_w = -1;
int dbg_measures_pane_h = -1;
bool measures_revealed = false;
/// The measurement dialog while one is open (null otherwise). A probe needs a handle on it:
/// it is a window of its own, so the main window's widget tree does not reach its buttons.
@@ -4878,6 +4885,11 @@ gboolean tick(gpointer user) {
std::fflush(stderr);
}
}
// Captured every frame so the dump has the last live value: see `dbg_measures_pane_w`.
if (app->measures_list != nullptr) {
app->dbg_measures_pane_w = gtk_widget_get_width(app->measures_list);
app->dbg_measures_pane_h = gtk_widget_get_height(app->measures_list);
}
if (model.generation() != app->signal_generation) rebuild_signal_panels(*app);
// A gesture moves the *plot's* cursor; adopting it here is what makes the two
// vertical markers one time across every row, and what the pane then reads.
@@ -5243,7 +5255,7 @@ gboolean tick(gpointer user) {
if (r.plot) gutter = std::max(gutter, double(r.plot->layout().y_axis_width));
}
app->rows->note_y_gutter(gutter);
app->rows->apply_y_gutter();
app->rows->apply_gutters();
// The editor's buttons are placed from the rects this frame was laid out with, so they
// follow a resize and the gutter settling without being told.
@@ -8566,8 +8578,7 @@ int run_ui(int argc, char** argv, const Config& cfg, ScopeModel& model, Acquisit
"pane_w=%d pane_h=%d\n",
app.measures.size(), app.measures_revealed ? 1 : 0,
rows.measure_marks_placed(), app.measure_labels.size(),
app.measures_list != nullptr ? gtk_widget_get_width(app.measures_list) : -1,
app.measures_list != nullptr ? gtk_widget_get_height(app.measures_list) : -1);
app.dbg_measures_pane_w, app.dbg_measures_pane_h);
}
std::printf("[udpscope] header: window_button_w=%d window_label_w=%d (bar_w=%d)\n",
app.dbg_win_btn_w, app.dbg_win_label_w, app.dbg_bar_w);