Commit Graph
10 Commits
Author SHA1 Message Date
Martino Ferrari 0e256764d5 udpscope: an oscilloscope view for a UDPStreamer source
GTK4 and libadwaita. It receives one UDP stream through the vendored
udpsclient, buffers it in a large ring, and draws it with the vendored
plotter: a trigger with a pre/post event window, a pause that freezes the
window, cursors, measurements, math channels in a node editor, an event cache,
named configurations, preferences, and an export of the visible window to
.npy/.csv. Waveform files can be opened and analysed in place.

The interface is two threads -- a producer that owns the socket and the GTK
main thread that owns every widget and the GL context -- sharing one model
under one mutex, with samples crossing through lock-free rings.

deps/plotter is a submodule pinned to the commit this tree builds against;
deps/udpsclient is vendored in place. docs/claude/ documents the rules that
cost a defect to learn, and SPECS.md is the requirement document.
2026-10-11 15:21:40 +02:00
Martino Ferrari 8f54841c2c working on todo.md features 2026-10-10 22:52:12 +02:00
Martino Ferrari 4f496b14b7 feat: the measurements pane can show how each reading varies, and remembers that it was asked to 2026-10-08 09:34:06 +02:00
Martino FerrariandClaude Opus 4.6 4003084648 feat: a signal's chip is clickable, so a trace's colour is the user's to choose
Clicking the coloured chip in the signal list opens a GtkColorDialog and the
colour it returns overrides that signal's palette entry. Held by name, like
every other identity here, and persisted as a session `[[color]]` table.

RowSet::signal_color() becomes the one resolver: install() builds the trace
through it and all three chip sites read it, because a chip exists to say which
trace a row is about and an override is exactly the state in which two routes to
a colour would disagree. A grey chip names no trace and so is not clickable, and
the way back is `Default colour` in the signal's menu -- a setting with no undo
is a trap.

The gesture sits on the existing label rather than in a GtkButton: padding on
this row is what made the signal list twice as tall once. It claims its sequence
so a click on the chip cannot become the start of a row drag.

UDPSCOPE_PROBE_COLOR_MS reads the result out of plot::Plot::trace() and out of
the chip's own markup, with an override in no palette slot, so neither leg can
pass by asking the resolver what it already said.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-10-07 08:22:32 +02:00
Martino FerrariandClaude Sonnet 4.6 a0fa845b58 feat: About dialog with version, commit, build time and settings path
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-10-04 11:52:43 +02:00
Martino FerrariandClaude Sonnet 4.6 ffec797462 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>
2026-10-04 10:03:48 +02:00
Martino Ferrari eabdde34f9 list the sources with a stored session
known_sources() reads the session files the layout store already owns, keeping
only those that name a signal: a development machine accumulates one file per
probe run on a throwaway port, and all 140 of them here hold empty slots.
2026-10-04 00:08:15 +02:00
Martino Ferrari c604d62917 session file records how the source was reached
A session is named <host>_<port>.toml, which cannot say whether that source
was multicast or which interface it joined on, so a chooser row could not
reconnect to one. Both go in the top block: a bare key after a [[table]]
header belongs to that table, which is how the enabled key was lost once.
2026-10-04 00:04:52 +02:00
Martino Ferrari aa2508e431 added math signals 2026-10-03 17:28:17 +02:00
Martino Ferrari 2218aed839 Updated local storage 2026-10-02 11:30:54 +02:00