Feature/source plugins #1
No files matched your search
@@ -27,7 +27,10 @@ Options: `--server`, `--port`, `--multicast`, `--interface`, `--buffer-mb`
|
|||||||
window the toolbar offers, default 600), `--holdoff` (default trigger hold-off),
|
window the toolbar offers, default 600), `--holdoff` (default trigger hold-off),
|
||||||
`--recv-buffer`, `--plots` (the number of stacked plots to start with, 1..8; rows are
|
`--recv-buffer`, `--plots` (the number of stacked plots to start with, 1..8; rows are
|
||||||
added and removed from the **Layout editor** afterwards), `--relative-time` (see below),
|
added and removed from the **Layout editor** afterwards), `--relative-time` (see below),
|
||||||
`--capture-packets N` (see "Packet capture" below).
|
`--capture-packets N` (see "Packet capture" below), `--renderer NAME` (GTK's renderer:
|
||||||
|
`cairo`, `gl` or `vulkan`; `cairo` is the software preset that exports both
|
||||||
|
`GSK_RENDERER` and `LIBGL_ALWAYS_SOFTWARE` before GTK initialises — see the SSH
|
||||||
|
limitation below).
|
||||||
|
|
||||||
**What a large `--buffer-mb` costs at startup, and what the window says while it happens.** The
|
**What a large `--buffer-mb` costs at startup, and what the window says while it happens.** The
|
||||||
buffer's rings are value-initialised, so allocating one touches every byte of `--buffer-mb` --- about
|
buffer's rings are value-initialised, so allocating one touches every byte of `--buffer-mb` --- about
|
||||||
@@ -2922,6 +2925,29 @@ silence timeout and then resumes, which is the EMI case end to end.
|
|||||||
inner popover takes the grab, the menu closes under the click, and the bubble is
|
inner popover takes the grab, the menu closes under the click, and the bubble is
|
||||||
left parented to a row in a hidden popover. Export is described in the
|
left parented to a row in a hidden popover. Export is described in the
|
||||||
Documentation instead.
|
Documentation instead.
|
||||||
|
- **Over SSH with X11 forwarding the window may never paint or allocate, and the cure is
|
||||||
|
`--renderer cairo`.** There are two walls, in order.
|
||||||
|
GTK4 defaults to Vulkan and a forwarded display has no usable Vulkan device, so the
|
||||||
|
log shows `vkCreateSwapchainKHR(): Initialization of an object could not be completed
|
||||||
|
... (VK_ERROR_INITIALIZATION_FAILED)` and nothing is drawn. `GSK_RENDERER` alone gets
|
||||||
|
past that, but the plot is a `GtkGLArea` and asks GDK for a GL context of its own
|
||||||
|
whatever GSK draws with; over forwarding there is no hardware one, and the window then
|
||||||
|
stays unallocated — `header: window_button_w=0` with a zero `bar_w`, and no
|
||||||
|
`[width-probe]` line (it prints only once the plot has a viewport wider than 100 px) —
|
||||||
|
while the stream side is fine. Absence of `buffer allocated:` is *not* part of that
|
||||||
|
reading: signals arrive disabled, so a start with nothing enabled plans no buffer and
|
||||||
|
prints no line even on a healthy display, and the plot stays empty until a signal's
|
||||||
|
switch is turned on.
|
||||||
|
`--renderer cairo` is the preset for exactly this display: it exports
|
||||||
|
`GSK_RENDERER=cairo` **and** `LIBGL_ALWAYS_SOFTWARE=1` (llvmpipe answers the GL area),
|
||||||
|
the same pair `tools/make-help-shots.sh` uses. `--renderer gl` is the faster
|
||||||
|
alternative when indirect GL works. The variables are exported in `main()` **before**
|
||||||
|
`gtk_init()`/`adw_init()`, which is the whole trap: they are read when the renderer and
|
||||||
|
the GL context are created, so setting them later is a silent no-op. Without the option
|
||||||
|
nothing is set and GTK's own choice stands. No `GDK_BACKEND` companion exists on
|
||||||
|
purpose: an X11-forwarded session carries `DISPLAY` and no `WAYLAND_DISPLAY`, so GDK
|
||||||
|
already selects X11; forcing it would override a working local Wayland session for no
|
||||||
|
gain (if both are set and Wayland is broken, export `GDK_BACKEND=x11` yourself).
|
||||||
- **The tour card can cover its own target, and in the documented headless run it
|
- **The tour card can cover its own target, and in the documented headless run it
|
||||||
does.** The card is placed under, over or beside the hole whenever one of those has
|
does.** The card is placed under, over or beside the hole whenever one of those has
|
||||||
room; when the target covers the whole content -- which is what the plot does at the
|
room; when the target covers the whole content -- which is what the plot does at the
|
||||||
|
|||||||
@@ -103,6 +103,12 @@ count to *start* with, 1..`RowSet::kMaxRows`; the layout editor owns it afterwar
|
|||||||
the value is required, and the popover button uses 1000 when the flag was absent).
|
the value is required, and the popover button uses 1000 when the flag was absent).
|
||||||
The flag is **not** persisted in the session file: persistence of the enable state
|
The flag is **not** persisted in the session file: persistence of the enable state
|
||||||
belongs with `todo.md` item 3, alongside the relative-time toggle.
|
belongs with `todo.md` item 3, alongside the relative-time toggle.
|
||||||
|
`--renderer cairo|gl|vulkan` exports `GSK_RENDERER` **at the top of `main()`** —
|
||||||
|
before `choose_source()`'s `adw_init()` and `run_ui()`'s, because GSK reads the variable when
|
||||||
|
it creates its renderer and setting it later is a silent no-op. `cairo` is the software preset
|
||||||
|
and also exports `LIBGL_ALWAYS_SOFTWARE=1`: the plot is a `GtkGLArea`, so a display with no
|
||||||
|
usable GPU fails twice, and this is the pair `tools/make-help-shots.sh` already sets. Nothing
|
||||||
|
is exported when the option is absent, so GTK's own choice stands.
|
||||||
|
|
||||||
`tools/mock_udpstreamer.py` has waveform-injection options used by the probes:
|
`tools/mock_udpstreamer.py` has waveform-injection options used by the probes:
|
||||||
|
|
||||||
|
|||||||
@@ -450,11 +450,25 @@ Two lines of the self-check output are assertions in disguise:
|
|||||||
floor are the two refusals, and because the probe emits `::clicked` straight at the
|
floor are the two refusals, and because the probe emits `::clicked` straight at the
|
||||||
widget it reaches the handler even when the button is insensitive, so `add above 0` there
|
widget it reaches the handler even when the button is insensitive, so `add above 0` there
|
||||||
must leave `rows=` and `traces=` untouched rather than growing a ninth row.
|
must leave `rows=` and `traces=` untouched rather than growing a ninth row.
|
||||||
- `header: window_button_w=W bar_w=B` (stderr, once a second) — **`W` must be
|
- `[udpscope] header: window_button_w=W bar_w=B` (stderr, once a second, **only when
|
||||||
|
`UDPSCOPE_PROBE_HEADER` is set**) — **`W` must be
|
||||||
a small constant and must not track `B`**: the window control is a header-bar child,
|
a small constant and must not track `B`**: the window control is a header-bar child,
|
||||||
and an expanding one is measured into the title's centre box, so it stretched from the
|
and an expanding one is measured into the title's centre box, so it stretched from the
|
||||||
label to the title (516 px at a 1270 px bar before the fix, 121 px after). A `W` that
|
label to the title (516 px at a 1270 px bar before the fix, 121 px after). A `W` that
|
||||||
grows with the window width is that bug back.
|
grows with the window width is that bug back. The widths are still sampled every second
|
||||||
|
in every run, because the exit dump's `header:` line reads them; only the line is behind
|
||||||
|
the variable, since a reading printed on every run is a log flood (the `[notrace]` rule).
|
||||||
|
An unallocated window reads `0` for both, which is the reading for "the toplevel never
|
||||||
|
got a size", not for a layout drift.
|
||||||
|
- `[renderer] option=NAME GSK_RENDERER=V LIBGL_ALWAYS_SOFTWARE=V` (stderr, at startup,
|
||||||
|
before GTK initialises; only when `UDPSCOPE_PROBE_RENDERER` is set) — what `--renderer`
|
||||||
|
exported, printed from `main()` so the reading is of the environment a pre-init GTK will
|
||||||
|
see. `option=(unset)` with both variables `(unset)` is the default and the assertion that
|
||||||
|
the flag changes nothing unless asked.
|
||||||
|
- `[renderer] realized=TYPE` (stderr, once, from the first tick that finds a window renderer;
|
||||||
|
same probe) — the class GTK actually built (`GskCairoRenderer` for `--renderer cairo`),
|
||||||
|
which is the half of the claim the export alone cannot make. It is null until the window is
|
||||||
|
realized, so a tick that finds none tries again rather than printing a false reading.
|
||||||
- `toolbar icons: trigger=NAME(ok) events=... pause=... cursors=... sidebar=... menu=...` (in the
|
- `toolbar icons: trigger=NAME(ok) events=... pause=... cursors=... sidebar=... menu=...` (in the
|
||||||
exit dump, beside `panel controls:`) — the icon name every toolbar button
|
exit dump, beside `panel controls:`) — the icon name every toolbar button
|
||||||
carries, with `gtk_icon_theme_has_icon()`'s verdict. **Every entry must read `(ok)`.** Three
|
carries, with `gtk_icon_theme_has_icon()`'s verdict. **Every entry must read `(ok)`.** Three
|
||||||
|
|||||||
@@ -155,6 +155,17 @@ precedent, not an exception.
|
|||||||
(the frame's ink is the image) and would otherwise be counted as well. The counter itself is in
|
(the frame's ink is the image) and would otherwise be counted as well. The counter itself is in
|
||||||
every build; see [probe-readings.md](probe-readings.md).
|
every build; see [probe-readings.md](probe-readings.md).
|
||||||
|
|
||||||
|
- `UDPSCOPE_PROBE_HEADER` (no value; **not** a scheduled probe) — the once-a-second
|
||||||
|
`[udpscope] header: window_button_w=… bar_w=…` line. The widths are sampled either way (the
|
||||||
|
exit dump reads them); the line is behind this variable because it is detail behind that
|
||||||
|
reading, not a line every run should print. An unallocated window reads `0` for both.
|
||||||
|
- `UDPSCOPE_PROBE_RENDERER` (no value; **not** a scheduled probe) — the renderer pair
|
||||||
|
`--renderer` should have exported, printed from `main()` before GTK initialises
|
||||||
|
(`[renderer] option=… GSK_RENDERER=… LIBGL_ALWAYS_SOFTWARE=…`), then the renderer GTK
|
||||||
|
built (`[renderer] realized=…`) once the window has one. Run it with `--renderer cairo` to
|
||||||
|
check the option reaches the environment before `gtk_init()`/`adw_init()`; run it without
|
||||||
|
the option to check the default is untouched.
|
||||||
|
|
||||||
- `UDPSCOPE_PROBE_TRIGRENUM_MS` — the trigger's capture must survive a **renumbering of its own
|
- `UDPSCOPE_PROBE_TRIGRENUM_MS` — the trigger's capture must survive a **renumbering of its own
|
||||||
channel**. It sets the reported geometry through the real widgets (1 s window, 10 % pre share,
|
channel**. It sets the reported geometry through the real widgets (1 s window, 10 % pre share,
|
||||||
hold-off from `--holdoff`) on the mock's *square* alone, polls for a capture to open, and then
|
hold-off from `--holdoff`) on the mock's *square* alone, polls for a capture to open, and then
|
||||||
|
|||||||
@@ -55,7 +55,20 @@
|
|||||||
|
|
||||||
<p>The signals arrive <em>disabled</em>. Nothing is acquired until you turn one on in the
|
<p>The signals arrive <em>disabled</em>. Nothing is acquired until you turn one on in the
|
||||||
signal list, and enabling is what starts acquisition — so a freshly opened scope with an
|
signal list, and enabling is what starts acquisition — so a freshly opened scope with an
|
||||||
idle side panel is working correctly, not failing to connect.</p>
|
idle side panel is working correctly, not failing to connect. For the same reason a start
|
||||||
|
that has enabled nothing prints no <code>buffer allocated:</code> line on stderr.</p>
|
||||||
|
|
||||||
|
<note style="warning">
|
||||||
|
<p>Over SSH with X11 forwarding the window may never appear, and the cure is
|
||||||
|
<cmd>udpscope --renderer cairo</cmd>. Two symptoms tell the two halves apart: a
|
||||||
|
<code>vkCreateSwapchainKHR() ... VK_ERROR_INITIALIZATION_FAILED</code> warning is
|
||||||
|
GTK's default Vulkan renderer failing to make a swapchain and is what the option's
|
||||||
|
<code>GSK_RENDERER=cairo</code> answers; a window whose allocation stays zero
|
||||||
|
(<code>window_button_w=0</code>) means the plot's GL area has no context either, and
|
||||||
|
that is what its <code>LIBGL_ALWAYS_SOFTWARE=1</code> answers. Both variables are
|
||||||
|
exported before GTK initialises, because they are read when the renderer and the GL
|
||||||
|
context are created. <cmd>--renderer gl</cmd> is faster when indirect GL works.</p>
|
||||||
|
</note>
|
||||||
|
|
||||||
<note style="tip">
|
<note style="tip">
|
||||||
<p>See <link xref="source">Choosing a source</link> for the chooser and the multicast
|
<p>See <link xref="source">Choosing a source</link> for the chooser and the multicast
|
||||||
|
|||||||
@@ -62,6 +62,12 @@ void print_usage(std::FILE* out, const char* argv0) {
|
|||||||
" time is then kept for export only\n"
|
" time is then kept for export only\n"
|
||||||
" --open FILE open an exported waveform (.npy) instead of a live source:\n"
|
" --open FILE open an exported waveform (.npy) instead of a live source:\n"
|
||||||
" signals from its columns, no socket, no acquisition\n"
|
" signals from its columns, no socket, no acquisition\n"
|
||||||
|
" --renderer NAME render with cairo (software), gl or vulkan. cairo also\n"
|
||||||
|
" exports LIBGL_ALWAYS_SOFTWARE=1, because the plot is a\n"
|
||||||
|
" GtkGLArea and still needs a GL context. Over SSH/X11\n"
|
||||||
|
" forwarding use cairo: Vulkan cannot make a swapchain there\n"
|
||||||
|
" and the GL area has no hardware context, so the window\n"
|
||||||
|
" never paints or allocates\n"
|
||||||
" -h, --help this text\n",
|
" -h, --help this text\n",
|
||||||
argv0);
|
argv0);
|
||||||
}
|
}
|
||||||
@@ -113,6 +119,15 @@ Config parse_args(int argc, char** argv) {
|
|||||||
c.capture_packets = static_cast<std::uint32_t>(n);
|
c.capture_packets = static_cast<std::uint32_t>(n);
|
||||||
} else if (a == "--open" || a.rfind("--open=", 0) == 0) {
|
} else if (a == "--open" || a.rfind("--open=", 0) == 0) {
|
||||||
c.open_waveform = value_of(argc, argv, i, "--open");
|
c.open_waveform = value_of(argc, argv, i, "--open");
|
||||||
|
} else if (a == "--renderer" || a.rfind("--renderer=", 0) == 0) {
|
||||||
|
const std::string v = value_of(argc, argv, i, "--renderer");
|
||||||
|
// Only the three a source or a forwarded display actually chooses between. Anything
|
||||||
|
// GTK does not know would fall back with a warning and leave the user reading a
|
||||||
|
// renderer failure that is really a typo, so it is refused here instead.
|
||||||
|
if (v != "cairo" && v != "gl" && v != "vulkan") {
|
||||||
|
fail("--renderer must be cairo, gl or vulkan: " + v);
|
||||||
|
}
|
||||||
|
c.renderer = v;
|
||||||
} else if (a == "--relative-time") {
|
} else if (a == "--relative-time") {
|
||||||
c.relative_time = true;
|
c.relative_time = true;
|
||||||
c.relative_time_specified = true;
|
c.relative_time_specified = true;
|
||||||
|
|||||||
@@ -60,6 +60,15 @@ struct Config {
|
|||||||
/// and the chooser's Waveforms page sets this rather than the connection fields.
|
/// and the chooser's Waveforms page sets this rather than the connection fields.
|
||||||
std::string open_waveform;
|
std::string open_waveform;
|
||||||
|
|
||||||
|
/// `--renderer NAME`: the GSK renderer to force, by exporting `GSK_RENDERER` **before any GTK
|
||||||
|
/// call**. Empty leaves GTK's own choice alone. `cairo` is the software preset over SSH/X11
|
||||||
|
/// forwarding: it also exports `LIBGL_ALWAYS_SOFTWARE=1`, because the plot's `GtkGLArea` asks
|
||||||
|
/// for a GL context whatever GSK draws with. `gl` is the faster alternative when indirect GL
|
||||||
|
/// works. The variables are read when the renderer/context is created, so setting them anywhere
|
||||||
|
/// after `gtk_init()`/`adw_init()` is a no-op -- which is why this is a `Config` field applied
|
||||||
|
/// at the top of `main()` and not a preference.
|
||||||
|
std::string renderer;
|
||||||
|
|
||||||
/// The source chooser's "Guided tour" entry. The tour is drawn on the application's own window,
|
/// The source chooser's "Guided tour" entry. The tour is drawn on the application's own window,
|
||||||
/// which does not exist while the chooser runs, so the request rides on the one value `main()`
|
/// which does not exist while the chooser runs, so the request rides on the one value `main()`
|
||||||
/// owns and hands to both the chooser and the front end (`open_waveform` and the preferences'
|
/// owns and hands to both the chooser and the front end (`open_waveform` and the preferences'
|
||||||
|
|||||||
@@ -10,6 +10,8 @@
|
|||||||
#include "source_dialog.hpp"
|
#include "source_dialog.hpp"
|
||||||
#include "ui.hpp"
|
#include "ui.hpp"
|
||||||
|
|
||||||
|
#include <glib.h>
|
||||||
|
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <exception>
|
#include <exception>
|
||||||
|
|
||||||
@@ -26,6 +28,31 @@ int main(int argc, char** argv) {
|
|||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// `--renderer` is exported here and nowhere else, because GSK reads `GSK_RENDERER` when it
|
||||||
|
// *creates* its renderer: inside the first `adw_init()`/`gtk_init()`, which `choose_source()`
|
||||||
|
// below may call. Setting it after that point is a silent no-op, so the one place it can be
|
||||||
|
// honoured is before anything touches GTK. Overwrite: a flag on the command line is a
|
||||||
|
// statement about this run, and the environment is only a default it may override.
|
||||||
|
//
|
||||||
|
// `cairo` is the whole software preset and not just GSK's half: the plot is a `GtkGLArea`,
|
||||||
|
// which asks GDK for a GL context of its own whatever GSK draws with. Over X11 forwarding
|
||||||
|
// that request is what leaves the window unallocated (no `buffer allocated:` line, zero
|
||||||
|
// width) once the Vulkan swapchain failure is out of the way, so `LIBGL_ALWAYS_SOFTWARE`
|
||||||
|
// makes Mesa answer it with llvmpipe. This is the pair `tools/make-help-shots.sh` sets.
|
||||||
|
if (!cfg.renderer.empty()) g_setenv("GSK_RENDERER", cfg.renderer.c_str(), TRUE);
|
||||||
|
if (cfg.renderer == "cairo") g_setenv("LIBGL_ALWAYS_SOFTWARE", "1", TRUE);
|
||||||
|
// The startup half of the renderer probe, printed before GTK exists: the request and the
|
||||||
|
// variables this process actually exported. The window's `[renderer] realized=` line is the
|
||||||
|
// other half and says which renderer GTK built.
|
||||||
|
if (g_getenv("UDPSCOPE_PROBE_RENDERER") != nullptr) {
|
||||||
|
const char* gsk = g_getenv("GSK_RENDERER");
|
||||||
|
const char* gl = g_getenv("LIBGL_ALWAYS_SOFTWARE");
|
||||||
|
std::fprintf(stderr, "[renderer] option=%s GSK_RENDERER=%s LIBGL_ALWAYS_SOFTWARE=%s\n",
|
||||||
|
cfg.renderer.empty() ? "(unset)" : cfg.renderer.c_str(),
|
||||||
|
gsk != nullptr ? gsk : "(unset)", gl != nullptr ? gl : "(unset)");
|
||||||
|
std::fflush(stderr);
|
||||||
|
}
|
||||||
|
|
||||||
// No `--server` means the user has not said which source to open, so ask. Before the model:
|
// No `--server` means the user has not said which source to open, so ask. Before the model:
|
||||||
// declining must cost no buffer and no socket, and nothing below has to cope with "no source".
|
// declining must cost no buffer and no socket, and nothing below has to cope with "no source".
|
||||||
// `--open` is the other answer to that question, and the chooser knows it: its Waveforms page
|
// `--open` is the other answer to that question, and the chooser knows it: its Waveforms page
|
||||||
|
|||||||
+23
@@ -3465,6 +3465,22 @@ gboolean tick(gpointer user) {
|
|||||||
ScopeModel& model = *app->model;
|
ScopeModel& model = *app->model;
|
||||||
const gint64 tick_start_us = g_get_monotonic_time();
|
const gint64 tick_start_us = g_get_monotonic_time();
|
||||||
|
|
||||||
|
// The renderer GTK actually built, printed once and only under the probe: `--renderer` exports
|
||||||
|
// `GSK_RENDERER` before any GTK call, and this is the other half of that claim -- the class GTK
|
||||||
|
// chose, not the name it was asked for. The renderer is null until the window is realized, so a
|
||||||
|
// tick that finds none simply tries again.
|
||||||
|
{
|
||||||
|
static const bool renderer_probe = g_getenv("UDPSCOPE_PROBE_RENDERER") != nullptr;
|
||||||
|
static bool renderer_printed = false;
|
||||||
|
if (renderer_probe && !renderer_printed) {
|
||||||
|
if (GskRenderer* r = gtk_native_get_renderer(GTK_NATIVE(app->window))) {
|
||||||
|
std::fprintf(stderr, "[renderer] realized=%s\n", G_OBJECT_TYPE_NAME(r));
|
||||||
|
std::fflush(stderr);
|
||||||
|
renderer_printed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// The paint probe's clock, claimed on the first tick that finds one: a widget has no
|
// The paint probe's clock, claimed on the first tick that finds one: a widget has no
|
||||||
// frame clock until it is realized, and the area is realized on its first draw.
|
// frame clock until it is realized, and the area is realized on its first draw.
|
||||||
if (app->dbg_clock == nullptr && app->area_widget != nullptr) {
|
if (app->dbg_clock == nullptr && app->area_widget != nullptr) {
|
||||||
@@ -3500,16 +3516,23 @@ gboolean tick(gpointer user) {
|
|||||||
// the title's centre box, i.e. window_button_w tracked the window width).
|
// the title's centre box, i.e. window_button_w tracked the window width).
|
||||||
if (app->window_button != nullptr) {
|
if (app->window_button != nullptr) {
|
||||||
static gint64 next_header_log = 0;
|
static gint64 next_header_log = 0;
|
||||||
|
// The widths stay sampled on this one-second cadence whatever the environment, because the
|
||||||
|
// exit dump's `header:` line reads them; only the *line* is behind a variable. A reading
|
||||||
|
// printed on every run is a log flood and not a reading -- the same rule the `[notrace]`
|
||||||
|
// per-paint line follows.
|
||||||
|
static const bool header_print = g_getenv("UDPSCOPE_PROBE_HEADER") != nullptr;
|
||||||
const gint64 now = g_get_monotonic_time();
|
const gint64 now = g_get_monotonic_time();
|
||||||
if (now >= next_header_log) {
|
if (now >= next_header_log) {
|
||||||
next_header_log = now + G_TIME_SPAN_SECOND;
|
next_header_log = now + G_TIME_SPAN_SECOND;
|
||||||
app->dbg_win_btn_w = gtk_widget_get_width(app->window_button);
|
app->dbg_win_btn_w = gtk_widget_get_width(app->window_button);
|
||||||
app->dbg_bar_w = gtk_widget_get_width(app->window);
|
app->dbg_bar_w = gtk_widget_get_width(app->window);
|
||||||
|
if (header_print) {
|
||||||
std::fprintf(stderr, "[udpscope] header: window_button_w=%d bar_w=%d\n",
|
std::fprintf(stderr, "[udpscope] header: window_button_w=%d bar_w=%d\n",
|
||||||
app->dbg_win_btn_w, app->dbg_bar_w);
|
app->dbg_win_btn_w, app->dbg_bar_w);
|
||||||
std::fflush(stderr);
|
std::fflush(stderr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// The help layer's own readings and the bounds the help's annotated figures crop, on the same
|
// The help layer's own readings and the bounds the help's annotated figures crop, on the same
|
||||||
// once-a-second cadence and for the same reason as the line above: the dump runs after the window
|
// once-a-second cadence and for the same reason as the line above: the dump runs after the window
|
||||||
// is destroyed and `verification.md` forbids a `gtk_widget_*` call there.
|
// is destroyed and `verification.md` forbids a `gtk_widget_*` call there.
|
||||||
|
|||||||
Reference in new issue
Block a user