fix code-review findings in the source-chooser feature
Seven correctness and documentation fixes found in the final review:
- `known_sources()`: check the strtol end pointer and add a round-trip
guard so a filename that layout_path() could not have produced is
rejected; without it `foo_8000x.toml` listed as `foo:8000` whose `x`
would unlink `foo_8000.toml`, a different configured session.
- `chooser_finish()`: destroy the form window before the chooser window;
a row click or Escape while the form was up left a window whose handlers
pointed at a dead stack frame (latent use-after-free).
- `choose_source()`: keep the pick_probe timeout id and g_source_remove
it after the loop; a row picked before the delay elapsed left an armed
source on the main context pointing at the gone frame.
- `chooser_populate()`: replace a false comment ("not a GtkListBoxRow")
with the truth — GTK4 auto-wraps, but the wrapped row carries no
source-index data so the bounds check refuses it.
- `pick_probe()`: collapse the duplicate `known.empty()` / `row==nullptr`
guards; the second was unreachable and two identical lines for one cause
makes probe output ambiguous.
- `save_layout()`: state the TOML rule directly in the multicast/interface
comment rather than cross-referencing the adjacent enabled comment.
- `save_layout_now()`: refuse to write when signals() is empty; a run
against a dead port receives no CONFIG, so every slot name is empty and
the save would overwrite a configured session with signals=["",...],
which the chooser filter refuses, silently removing that source from
the only list it can be picked from.
- Fix include order in layout_store.cpp (<cstdio> before <cstdlib>).
- Update CLAUDE.md and README.md to document the empty-signals guard and
fix the probe-output quote to match the emitted text exactly.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
b8d0ce8625
commit
ffec797462
6 files changed
+151
-16
No files matched your search
@@ -573,6 +573,12 @@ LayoutState collect_layout(App& app) {
|
||||
/// stderr; success is silent -- a save per click must not narrate.
|
||||
void save_layout_now(App& app) {
|
||||
if (app.source_port == 0) return;
|
||||
// Nothing is known about this source yet -- no CONFIG ever arrived -- so every slot name is
|
||||
// empty and this would overwrite a configured session with `signals = ["", ...]`. The startup
|
||||
// chooser filters such a file out of its list, so a source whose streamer was down during one
|
||||
// run would silently vanish from the only place it can be picked. Keeping the old file loses
|
||||
// nothing: this run configured nothing.
|
||||
if (app.model->signals().empty()) return;
|
||||
if (!save_layout(layout_path(app.source_host, app.source_port), collect_layout(app))) {
|
||||
std::fprintf(stderr, "[udpscope] failed to write the layout file\n");
|
||||
std::fflush(stderr);
|
||||
|
||||
Reference in new issue
Block a user