From d8f82bd6108f09774268faaf925e0e51f9c9f6ad Mon Sep 17 00:00:00 2001 From: Martino Ferrari Date: Tue, 3 Nov 2026 02:14:37 +0100 Subject: [PATCH] docs: source badge name only, no signals lock, the Add-a-signal page Co-Authored-By: Claude Opus 4.6 --- README.md | 19 +++--- docs/claude/architecture.md | 2 +- docs/claude/invariants-gtk-and-chrome.md | 4 +- docs/claude/invariants-plot-and-layout.md | 6 +- docs/claude/invariants-session-and-export.md | 6 +- docs/claude/probe-readings.md | 62 +++++++++++--------- docs/claude/verification.md | 7 ++- help/C/org.udpscope.app/overview.page | 2 +- 8 files changed, 59 insertions(+), 49 deletions(-) diff --git a/README.md b/README.md index 8f3f433..cba3458 100644 --- a/README.md +++ b/README.md @@ -104,7 +104,13 @@ src/ui_signal_page.cpp the per-signal slide-over pages (`signal:`) **The window.** The plot is the window. Under it sits a fixed-height **badge bar** (36 px). Its left part is the **signals**: one badge per *enabled* signal (a dot in the signal's colour, filled while the -signal is plotted, and its name), then a `+` badge that opens the full signal list (`g` does the same). +signal is plotted, and its name), then a `+` badge that opens the **Add a signal** page (`g` does the same). That page proposes only what can be +*added*: the streamed signals that are not acquired and the math channels that are not computed, each row with +the tick that acquires it (the row then leaves the page and the signal's badge appears), plus the `MATH` +divider's `+` for a new math channel; when every signal is acquired a dim "every signal is acquired" line +stands in for the list. While the bar has overflowed into the "N signals" badge the page lists the acquired +signals too, since their rows are then the only way to each signal's page. There is no signal lock: a +signal's page and the list always allow acquire, colour, calibration, the math editor and delete. A signal badge opens that signal's own page in the slide-over -- colour, calibration and the *Plot on N* buttons -- and is also the drag handle: drag it onto a plot to draw the signal there, or onto another badge to reorder them. Past **six** signal badges the bar shows a single **"N signals"** badge in their @@ -112,7 +118,7 @@ place (it opens the list, and `+` is hidden): the E-1608's 8 analog inputs and c otherwise. The signal badges sit in a scroller, so the bar's minimum width, and with it the window's, never depends on how many signals there are. The badge order is kept **for the session only**; it is not written to the session file. The right-hand end carries two words-only badges: **Trigger** (`off`, `armed`, `capturing`, `triggered` -or `disarmed`) and **Source** (the kind's name and `connected`/`disconnected`; a read-out, not a button, +or `disarmed`) and **Source** (the kind's name only -- the link state is the status bar's; a read-out, not a button, for a source with no controls of its own, and for a file). A badge opens the **slide-over**: a 360 px panel laid over the plot's right edge, opaque, with its own title, a *Close* button and one scroller per page. The Trigger badge opens the trigger form; the Source badge opens the source kind's own panels @@ -2937,11 +2943,10 @@ now — they are listed so they are not mistaken for anything else: tabs did not fit 320 px. It is **one dock** now, with no tab switcher: the cursors and the measurements in one scroller, shown from the start and insensitive until the stream has signals; `m` scrolls it to the cursors; the **signal list** is the `signals` slide-over page (`g`, or the `+` badge). The **trigger form** and a **source's own - controls** are pages of the slide-over, reached from the badge bar. Both panes also have a **locked** mode, which is what a returning user gets: the - trigger pane shows one line saying what it is set to, the signal list shows only the signals that are - on, and neither offers a control that changes the configuration -- each heading has an `[edit]` button - to switch to the full pane, and back. Dragging a signal's badge onto a plot still works while locked -- - filling plots is not configuration. A signal's badge is dragged onto a plot, or onto another badge to + controls** are pages of the slide-over, reached from the badge bar. The trigger pane also has a **locked** mode, which is what a returning user gets: it + shows one line saying what it is set to and offers no control that changes the configuration -- its + heading has an `[edit]` button to switch to the full pane, and back. The signals have no lock: the + signals page only proposes what can be added, and the signal pages always allow editing. A signal's badge is dragged onto a plot, or onto another badge to reorder the badges (the order is kept for the session only); the slide-over closes for the length of the drag so there is something under the pointer to drop onto. A **first run** (no session file for that source) comes up unlocked, because configuring the host is what a first run is for. Each pane's heading also carries its **own switch**, the same control as the diff --git a/docs/claude/architecture.md b/docs/claude/architecture.md index 10319af..7446c30 100644 --- a/docs/claude/architecture.md +++ b/docs/claude/architecture.md @@ -123,7 +123,7 @@ overlay is built (the tour's overlay wraps the whole window, so its dim rectangl too); the trigger form is registered as a page by `build_trigger_panel`; `build_source_panels()` registers the source pages; and `build_badge_bar()` runs *after* all of them, because the Source badge needs to know the first page. -Per-tick state (the Trigger word, the Source name and `connected`, the open marks) is written by +Per-tick state (the Trigger word, the Source name, the open marks) is written by `sync_badge_bar()`, called from `tick()` and from `slideover_open`/`slideover_close`; it writes a label only when its text changed. Nothing in the badge bar or the slide-over may be read from the producer thread. diff --git a/docs/claude/invariants-gtk-and-chrome.md b/docs/claude/invariants-gtk-and-chrome.md index deb5551..2fa3653 100644 --- a/docs/claude/invariants-gtk-and-chrome.md +++ b/docs/claude/invariants-gtk-and-chrome.md @@ -255,8 +255,8 @@ Part of [CLAUDE.md](../../CLAUDE.md). These are rules that cost a defect to lear `gtk_box_reorder_child_after` and rewrites `App::badge_order` from the widgets, and **never rebuilds the badges** -- the dragged badge and the target whose `drop` emission is running must both survive it. A foreign drag whose string is not a signal index is refused. `[sigbadges]` reads the controllers, the payload and the reorder; `pane drag:` reads the close. -- **A stock icon name has to be one *Adwaita* has, and a blank button is the failure mode.** The two - panes' `[edit]` buttons are `document-edit-symbolic` (the pencil: edit) and `object-select-symbolic` +- **A stock icon name has to be one *Adwaita* has, and a blank button is the failure mode.** The + trigger pane's `[edit]` button is `document-edit-symbolic` (the pencil: edit) and `object-select-symbolic` (the check: done). The done one was `emblem-ok-symbolic`, which breeze ships and **Adwaita does not** — so the button that leaves configuration mode was blank, which no other reading could see: the button worked, the layout was unchanged, and only `pane edits:` (the two names and diff --git a/docs/claude/invariants-plot-and-layout.md b/docs/claude/invariants-plot-and-layout.md index 52cbe9b..1b962d5 100644 --- a/docs/claude/invariants-plot-and-layout.md +++ b/docs/claude/invariants-plot-and-layout.md @@ -116,8 +116,8 @@ Part of [CLAUDE.md](../../CLAUDE.md). These are rules that cost a defect to lear - **The badge bar has a fixed height and sits outside the overlay.** `badge_bar` is 36 px (`height-request`) between `plot_overlay` and the status line, so the plot above it never moves when a badge's text changes (`bar_h` = `bar_h_after` in `[badges]`). This is "layout must not depend on - content" for the new row: a badge's state is a word from a short fixed set and the Source badge's name - is a kind's display name, so what changes with the stream is the row's *width* (the bar has room to + content" for the new row: the Trigger badge's state is a word from a short fixed set and the Source badge + is a kind's display name only (no connection state: that is the status bar's), so what changes with the stream is the row's *width* (the bar has room to spare on the right) and never its height. The badge labels are not ellipsized: a name long enough to need it would be a defect of the kind's `display_name()`. Rows that appear by trigger condition appear inside the slide-over's scroller, never in the bar. @@ -127,7 +127,7 @@ Part of [CLAUDE.md](../../CLAUDE.md). These are rules that cost a defect to lear (the E-1608: eight inputs and the counter) would push the window's minimum width past a laptop screen. Past `kMaxSignalBadges` (6) the row is replaced by one "N signals" badge, which also stands for the `+`. The bar's height stays 36 px in every one of these states (`bar_h_fixed` in `[sigbadges]`), and - `bar_min_w` reads the same with one signal, nine signals and the overflow (329 on the E-1608, 336 on UDPS: + `bar_min_w` reads the same with one signal, nine signals and the overflow (244 on the E-1608, 251 on UDPS: the Source badge of each kind reads differently). A change that makes it grow with the signal count has taken the scroller out of the measurement. - **The dock's width is fixed too** (`side_column` `width-request` 300, the split view's 320 px floor diff --git a/docs/claude/invariants-session-and-export.md b/docs/claude/invariants-session-and-export.md index 99a2eec..23a3727 100644 --- a/docs/claude/invariants-session-and-export.md +++ b/docs/claude/invariants-session-and-export.md @@ -13,9 +13,9 @@ Part of [CLAUDE.md](../../CLAUDE.md). These are rules that cost a defect to lear (`rebuild_trigger_signals()` picks up `App::pending_trigger_channel`, or `apply_trigger_layout()` resolves it itself when they are already there) — a name that resolved to nothing would silently move the trigger to channel 0. -- **A session that loaded is what locks the two panes.** Locked is the returning user's mode: the - trigger pane shows one compact line, the signal list only the signals that are on, and neither offers - a control that changes the configuration. A *first run* — no session file for that source — never +- **A session that loaded is what locks the trigger pane.** Locked is the returning user's mode: the + trigger pane shows one compact line and offers no control that changes the configuration (the signals + have no lock). A *first run* — no session file for that source — never reaches `apply_layout()` and stays in configuration mode, which is what a first run is for. The mode is a mode and not a setting: it is deliberately not persisted, so a scope reopened tomorrow starts where a returning user expects to start. **Dragging a signal onto a plot is not configuration and survives diff --git a/docs/claude/probe-readings.md b/docs/claude/probe-readings.md index 143253c..717ee86 100644 --- a/docs/claude/probe-readings.md +++ b/docs/claude/probe-readings.md @@ -1706,8 +1706,8 @@ library measures and draws the rect in tests of its own. ``` [udpscope] pane drag: slide-over closed for the drag=1 [udpscope] pane edits: locked=document-edit-symbolic(ok) config=object-select-symbolic(ok) -[udpscope] panes: trigger=locked signals=locked body_visible=0 summary='signal: square | condition: window, falling | level: 0.3 .. 0.6 | mode: single | noise band: 0.2 | pre-trigger: 20 % | hold-off: 1000 ms' -[udpscope] panes: trigger=config signals=config body_visible=1 summary='' +[udpscope] panes: trigger=locked body_visible=0 summary='signal: square | condition: window, falling | level: 0.3 .. 0.6 | mode: single | noise band: 0.2 | pre-trigger: 20 % | hold-off: 1000 ms' +[udpscope] panes: trigger=config body_visible=1 summary='' [udpscope] panes: the same line as the probe found it: 'mode: stream (trigger off)' ``` @@ -1720,18 +1720,18 @@ or more than six, where the badges are replaced by the "N signals" overflow). It `sidebar hidden for the drag=1 restored after it=1` pair, which tested `row_drag_sidebar()` -- the list rows were the drag handle and the overlaid sidebar had to step aside; both are gone. -**`pane edits:`** is the two `[edit]` buttons in each mode, by name and verdict, and it exists because of +**`pane edits:`** is the trigger pane's `[edit]` button in each mode, by name and verdict, and it exists because of a real defect: the "done" icon was `emblem-ok-symbolic`, which **Adwaita does not have** (breeze does), so the button that leaves configuration mode was blank — and a blank icon button is invisible in every other reading, exactly as `toolbar icons:` describes for the toolbar. The probe clicks into configuration mode and back, so one line carries both names. **Both must read `(ok)`**, and the pair is `document-edit-symbolic` (the pencil: edit) and `object-select-symbolic` (the check: done). -Both panes have two modes. **Locked** is what a returning user gets — the trigger pane shows the one line -in `summary`, the signal list shows only the signals that are on, and neither offers a control that -changes the configuration — and **config** is the full pane, which is what a *first run* gets, because a -session file that loaded is what "we have been here before" means. Each heading's `[edit]` button flips -its pane. +The trigger pane has two modes. **Locked** is what a returning user gets — the pane shows the one line +in `summary` and offers no control that changes the configuration — and **config** is the full pane, which +is what a *first run* gets, because a session file that loaded is what "we have been here before" means. +The heading's `[edit]` button flips it. (The signals have no lock: the badges and the signal pages are +direct manipulation, and `panes:` no longer carries a `signals=` field.) - **With trigger mode off the summary is one line**: `mode: stream (trigger off)`. The four lines below describe a trigger that is not running, so quoting them would describe nothing. The dump carries the line @@ -1765,9 +1765,8 @@ its pane. `body_visible=0` is a locked pane showing nothing at all, which is the defect this reads for. - **`body_visible=0`** is the settings being off screen, which is what makes the pane a couple of hundred pixels instead of 580 in `panels:`. -- **The locked signal list drops the rows of the signals that are off**, so `rows` counts what is on: - `signals panel: … rows=3 signals=2 divider=1` is two on signals and the `MATH` divider, not four - signals. Its `+` goes with them — a `+` on a locked list opens the editor the lock exists to keep shut. +- **The signals list has no lock.** It lists what can be *added* (see `signals panel:`), and the signal + pages always allow acquire, colour, calibrate, math edit and delete. ## `panel switches:` and the trigger panel's default sensitivity @@ -2305,19 +2304,26 @@ prints without a probe: `depth`, the retention (`kept`/`bytes`/`budget`/`dropped ## `signals panel:` ``` -[udpscope] signals panel: labels=2 list=1 rows=5 signals=4 math=0 divider=1 -[udpscope] signals panel: labels=2 list=1 rows=9 signals=4 math=4 divider=1 +[udpscope] signals panel: labels=3 list=1 rows=5 signals=4 math=0 divider=1 drag_sources=0 addable=4 addable_ok=1 empty_label=0 +[udpscope] signals panel: labels=3 list=1 rows=2 signals=1 math=0 divider=1 drag_sources=0 addable=1 addable_ok=1 empty_label=0 +[udpscope] signals panel: labels=3 list=1 rows=1 signals=0 math=0 divider=1 drag_sources=0 addable=0 addable_ok=1 empty_label=1 ``` -The signals panel's frame read from the window, and in the same line the *shape* of the one channel list: -the heading and the hint the `.ui` gives it, the list the rows go in, how many rows that list holds -(`rows`), how many of them are streamed signals (`signals`), how many math channels the app has (`math`), -and `divider`, which is 1 when `rows == signals + 1 + math` and 0 otherwise. **`drag_sources` must be +The signals panel's frame read from the window, and in the same line the *shape* of the one list. The page +is "Add a signal": it proposes only what can be added, so the list holds the streamed signals that are not +acquired (`signals`), the math channels that are not computed (`math`), and the `MATH` divider with its `+` +(`divider`, 1 when `rows == signals + 1 + math`, 0 otherwise). An acquired signal has a badge in the bar and +is not listed -- **unless the badge bar has overflowed** (more than 6 enabled signals, the "N signals" badge), +when the acquired ones are listed too, since the bar no longer shows them. `addable` is the same count +worked out from the model (unacquired listed signals, or all of them when overflowed) and `addable_ok=1` is +`signals + math == addable`; `0` is a list filter that drifted from the rule. `empty_label=1` is the dim +"every signal is acquired" label shown, which must happen exactly when `addable=0` and the stream declares +signals (the divider row stays, so a math channel can still be created). **`drag_sources` must be `0`**: dragging a signal onto a plot is the signal *badge's* job now (see `[sigbadges]`, which counts the badges' own sources), and the list's rows only open the signal's page; a non-zero reading is a second, competing -drag handle on the list. `labels=1` is a `.ui` that lost -one of the two labels — counted over the panel's subtree *above* the list, because the rows' own name -and unit labels are the code's and the heading sits in a heading row beside the pane's `[edit]` button — `list=0` one that lost the list -- both of which otherwise show as a panel quietly +drag handle on the list. `labels=2` is a `.ui` that lost +one of the three labels (heading, hint, the empty-state label) — counted over the panel's subtree *above* the list, because the rows' own name +and unit labels are the code's — `list=0` one that lost the list -- both of which otherwise show as a panel quietly missing a piece -- and **`divider=0` is the merged list that lost its `+`**, the one control that can create a math channel. The first line above is a run with no math channels at all: the divider is there anyway, because a `+` that only appeared once a channel existed could never create the first one. `panels: … @@ -2640,17 +2646,16 @@ left the caller's `false` alone would read `dock_shown=0 default=1 ok=0`. The pr ## `[signalpage]` ``` -[signalpage] name=sine open=1 pages=1 enable_visible=1 plot_buttons=1 plot_on_row0=1 plot_on_row0_disabled=1 locked_enable_sensitive=0 removed=1 btn_row0=1 back_to_list=1 row_opens=1 pruned=1 other_page_kept=1 rows=1 +[signalpage] name=sine open=1 pages=1 enable_visible=1 plot_buttons=1 plot_on_row0=1 plot_on_row0_disabled=1 removed=1 btn_row0=1 back_to_list=1 row_opens=1 pruned=1 other_page_kept=1 rows=1 ``` `UDPSCOPE_PROBE_SIGNAL_PAGES_MS` builds and drives the page of the first streamed signal. `open=1` (the slide-over shows `signal:`), `pages=1` (one page in `App::signal_pages`), `enable_visible=1` (the acquire tick is shown in live mode; `0` in a file view), `plot_buttons=` equal to `rows=` (one "Plot on" button per figure row), `plot_on_row0=1` (`assign_signal_to_row()` returned true and the signal is plotted, from a state where the probe first took it off every plot), `plot_on_row0_disabled=1` (after the page refilled with the signal on plot 1, that plot's "Plot on" button is insensitive, "already on this plot"; `0` is a button that would put the signal in a second slot of -the same plot), `locked_enable_sensitive=0` (with `signals_locked` set and `signal_pages_sync()` run, the page's Acquire/Compute tick is insensitive; `1` is a page that bypasses -the signal lock -- the same goes for a math channel's Edit graph and Delete, which the probe does not read; the probe restores the lock), `removed=1` -(the page's "Remove from plots" took it off), `btn_row0=1` (the "Plot on 1" button, the user's path, put it back), `back_to_list=1` ("All signals" -target), `row_opens=1` (activating the list row opens the page), and `pruned=1`: a math channel's page was opened, its **Delete** button clicked, and +the same plot), `removed=1` +(the page's "Remove from plots" took it off), `btn_row0=1` (the "Plot on 1" button, the user's path, put it back), `back_to_list=1` ("Back to signals" +target), `row_opens=1` (the probe switches the signal off so it is addable, activates its list row and the page opens; the signal is then switched back on), and `pruned=1`: a math channel's page was opened, its **Delete** button clicked, and the slide-over closed and the page was dropped by `signal_pages_sync()`. `pruned=-1` means no math channel could be added; `0` is a stale page. `other_page_kept=1`: with the `trigger` page open, a signal page that is not on show was pruned (a math channel added, its page built, the channel removed) and the slide-over was still on `trigger`, both by `slideover_page()` and by the stack's visible child; `0` is `signal_pages_sync()` parking the stack on the signals list while the title and the badge marker still name `trigger`; `-1` when there is no `trigger` page or no math channel could be added. @@ -2658,14 +2663,14 @@ signals list while the title and the badge marker still name `trigger`; `-1` whe ## `[sigbadges]` ``` -[sigbadges] enabled=4 badges=4 overflow=0 overflow_text=- plus_visible=1 dot_plotted=4 bar_h=36 bar_h_fixed=1 bar_min_w=336 opens=1 plus_opens=1 +[sigbadges] enabled=4 badges=4 overflow=0 overflow_text=- plus_visible=1 dot_plotted=4 bar_h=36 bar_h_fixed=1 bar_min_w=251 opens=1 plus_opens=1 ``` `UDPSCOPE_PROBE_SIGNAL_BADGES_MS` reads the signal badges. `badges` must equal `enabled` (the enabled, listed signals) while `enabled` is at most 6 (`kMaxSignalBadges`); beyond that `badges=0 overflow=1 overflow_text= signals plus_visible=0` (the "N signals" badge replaces the badges and the "+"). `dot_plotted` is the number of badges whose signal is on a plot (its dot is then the trace colour, not the unplotted grey). `bar_h` is the bar's allocated height and `bar_h_fixed=1` that neither it nor its natural height moved while one signal was switched off and on again (`rebuild_signal_panels()` each time), and that the badge count followed; `0` is a bar whose height depends on its content. -`bar_min_w` is only a record: the scroller must keep it the same however many signals there are (it reads 336 with the UDPS mock's 4 and with the overflow forced on, and 329 against the E-1608 mock with one signal and with nine -- the overflow, `enabled=9 badges=0 overflow=1 overflow_text=9 signals plus_visible=0 bar_h=36 bar_h_fixed=1`; a value that grows with the signal count is a scroller that has stopped constraining the bar). `opens=1`: clicking the +`bar_min_w` is only a record: the scroller must keep it the same however many signals there are (it reads 251 with the UDPS mock's 4 and with the overflow forced on, and 244 against the E-1608 mock with one signal and with nine -- the overflow, `enabled=9 badges=0 overflow=1 overflow_text=9 signals plus_visible=0 bar_h=36 bar_h_fixed=1`; a value that grows with the signal count is a scroller that has stopped constraining the bar). `opens=1`: clicking the first signal badge opens `signal:` with the badge marked open and a second click closes it (`-1` when overflowed, there being no signal badges); `plus_opens=1` the same for the "+" (the overflow badge when overflowed) and the `signals` page. @@ -2697,8 +2702,7 @@ any is a badge that does not say its panel is open, or one that stays marked aft **Line 2** is the Source badge, and every field has a value it must read: -- `name=` the source kind's name and `state=` `connected`/`disconnected`, as the badge shows them. `connected` against a live mock; a badge stuck on - `disconnected` is the status not reaching the badge. +- `name=` the source kind's name, as the badge shows it. The badge carries no link state (the status bar says it); there is no `state=` field. - `pages=` the number of slide-over pages the source kind contributes (`source-`): `0` for UDPS, `2` for the E-1608 (`source-ao`, `source-dio`). `can_target=0` with `pages=0` is the UDPS badge being a read-out rather than a button; `can_target=1` with `pages>0` is the button being live. `can_target=1` with `pages=0` is a dead button that opens nothing. diff --git a/docs/claude/verification.md b/docs/claude/verification.md index 7c749b1..25f5d9f 100644 --- a/docs/claude/verification.md +++ b/docs/claude/verification.md @@ -472,7 +472,7 @@ precedent, not an exception. a sensitivity, a visibility or a value needs no open page. **The source kind's panels are slide-over pages too** (`source-`: the E-1608's `source-ao` and `source-dio`, Flat UDP's, the probe kind's), so the same rule holds for them. The probe appends a second - `[badges]` line for the Source badge: `[badges] source name= state= pages= + `[badges]` line for the Source badge: `[badges] source name= pages= switch_visible=<0|1> can_target=<0|1> second_page= second_ok=<0|1> open_marked=<0|1|-1> closed_unmarked=1`. `pages` is the number of source pages, `switch_visible` is the segmented switch being on screen after the first source page was opened (`1` for the E-1608's two pages, `0` for one page @@ -1035,8 +1035,9 @@ stages into one is the failure mode**: a probe that rebuilt the buffer at 11.5 s immediately would read `bare=0` on every search leg, which looks exactly like a broken comparator. **Why 4 s and not 3.** `enable_all_probe`'s last act is a timeout of its own, `ENABLE_ALL_MS + 2000` -- 3 s -under this recipe -- which walks `signal_list` for the front channel's acquire tick and clicks it. A probe -that installs a math graph at that same instant rebuilds the signal panel under it, and the off-step then +under this recipe -- which opens the front channel's signal page and clicks its "Acquire this signal" tick (an +acquired signal is not in the Add-a-signal list, so the list cannot be walked). A probe +that installs a math graph at that same instant rebuilds the signal panels under it, and the off-step then emits `active` on a freed `GtkCheckButton`: `GLib-GObject-CRITICAL: instance with invalid (NULL) class pointer`, with the backtrace in `enable_all_probe()::{lambda}`. It is a real fragility of that probe's lookup and not of the feature, and it is why every documented probe time is offset from `ENABLE_ALL_MS + 2000`, not diff --git a/help/C/org.udpscope.app/overview.page b/help/C/org.udpscope.app/overview.page index fd605c2..9f95d4a 100644 --- a/help/C/org.udpscope.app/overview.page +++ b/help/C/org.udpscope.app/overview.page @@ -127,7 +127,7 @@ and port.

-

Enable a signal in the signal list. The plot stays empty for the first +

Enable a signal in the Add a signal page (the + badge). The plot stays empty for the first fraction of a second while the first window fills — the buffer holds only what has arrived.