Feature/source plugins #1

Merged
martino merged 130 commits from feature/source-plugins into main 2026-11-04 07:35:21 +01:00
5 changed files with 433 additions and 18 deletions
Showing only changes of commit ae1d262be4 - Show all commits

No files matched your search

+161 -10
View File
@@ -5,6 +5,7 @@
#include <algorithm>
#include <cmath>
#include <span>
namespace udpscope {
namespace {
@@ -67,6 +68,9 @@ constexpr float kMeasureDashOff = 4.0f;
/// a box, so there was a widget gap; a figure tiles its viewport, and without a gap two
/// stacked frames read as one tall plot with a line through it.
constexpr plot::f32 kRowGap = 6.0f;
/// The gap between two cells of a split row, device pixels: the same as the rows', so the
/// plots read as a grid.
constexpr plot::f32 kColGap = 6.0f;
/// The suffix that names a persistence layer, in the accumulate path's own vocabulary --
/// ` (A)`, ` (B)`, ` #4` -- so that `layer_base_name()` takes it back off, and the signal list, the
@@ -153,11 +157,56 @@ void RowSet::clear_signal_color(const std::string& signal) {
RowSet::RowSet() { rebuild_all(); }
int RowSet::cells_in_row(int fr) const {
return fr >= 0 && fr < figure_row_count() ? cells_per_row_[static_cast<std::size_t>(fr)] : 0;
}
int RowSet::first_plot_of_row(int fr) const {
int p = 0;
for (int r = 0; r < fr && r < figure_row_count(); ++r) p += cells_per_row_[static_cast<std::size_t>(r)];
return p;
}
int RowSet::figure_row_of(int plot) const {
if (plot < 0 || plot >= rows_) return -1;
int p = 0;
for (int r = 0; r < figure_row_count(); ++r) {
p += cells_per_row_[static_cast<std::size_t>(r)];
if (plot < p) return r;
}
return -1;
}
int RowSet::cell_of(int plot) const {
const int fr = figure_row_of(plot);
return fr < 0 ? -1 : plot - first_plot_of_row(fr);
}
const std::vector<float>& RowSet::cell_weights(int fr) const {
static const std::vector<float> none;
return fr >= 0 && fr < static_cast<int>(cell_weights_.size())
? cell_weights_[static_cast<std::size_t>(fr)]
: none;
}
std::string RowSet::plot_label(int plot) const {
const int fr = figure_row_of(plot);
if (fr < 0) return std::to_string(plot + 1);
if (cells_per_row_[static_cast<std::size_t>(fr)] == 1) return std::to_string(fr + 1);
return std::to_string(fr + 1) + static_cast<char>('a' + cell_of(plot));
}
void RowSet::set_row_count(int rows) {
const int want = std::clamp(rows, 1, kMaxRows);
if (want == rows_) return;
const bool all_single =
std::all_of(cells_per_row_.begin(), cells_per_row_.end(), [](int c) { return c == 1; });
if (want == rows_ && all_single) return;
// `n` single-cell rows. The plots keep their flattened slots (and so their contents), which is
// what the old "n rows" meant.
rows_ = want;
// A `Figure`'s row count is fixed at construction, so a new count is a new figure.
cells_per_row_.assign(static_cast<std::size_t>(want), 1);
cell_weights_.assign(static_cast<std::size_t>(want), {});
// A `Figure`'s shape is fixed at construction, so a new count is a new figure.
rebuild_all();
}
@@ -194,23 +243,103 @@ void RowSet::clear_row(Row& r) {
bool RowSet::insert_row(int at) {
if (rows_ >= kMaxRows) return false;
const int pos = std::clamp(at, 0, rows_);
const int fr = std::clamp(at, 0, figure_row_count());
const int pos = first_plot_of_row(fr);
for (int i = rows_; i > pos; --i) carry_row(row(i), row(i - 1));
clear_row(row(pos));
++rows_;
cells_per_row_.insert(cells_per_row_.begin() + fr, 1);
cell_weights_.insert(cell_weights_.begin() + fr, std::vector<float>{});
rebuild_all();
return true;
}
bool RowSet::remove_row(int at) {
if (rows_ <= 1 || at < 0 || at >= rows_) return false;
for (int i = at; i + 1 < rows_; ++i) carry_row(row(i), row(i + 1));
--rows_;
clear_row(row(rows_));
if (figure_row_count() <= 1 || at < 0 || at >= figure_row_count()) return false;
const int pos = first_plot_of_row(at);
const int n = cells_per_row_[static_cast<std::size_t>(at)];
// The rows below move up by the removed row's cell count; the `n` slots freed at the end go back
// to empty.
for (int i = pos; i + n < rows_; ++i) carry_row(row(i), row(i + n));
for (int i = rows_ - n; i < rows_; ++i) clear_row(row(i));
rows_ -= n;
cells_per_row_.erase(cells_per_row_.begin() + at);
cell_weights_.erase(cell_weights_.begin() + at);
rebuild_all();
return true;
}
bool RowSet::insert_cell(int fr, int at) {
if (fr < 0 || fr >= figure_row_count()) return false;
if (cells_per_row_[static_cast<std::size_t>(fr)] >= kMaxCellsPerRow || rows_ >= kMaxRows) {
return false;
}
const int n = cells_per_row_[static_cast<std::size_t>(fr)];
const int k = std::clamp(at, 0, n);
const int p = first_plot_of_row(fr) + k;
for (int i = rows_; i > p; --i) carry_row(row(i), row(i - 1));
clear_row(row(p));
++rows_;
++cells_per_row_[static_cast<std::size_t>(fr)];
auto& w = cell_weights_[static_cast<std::size_t>(fr)];
if (!w.empty()) {
// The new cell takes the mean share, so it neither dwarfs nor vanishes beside its neighbours.
float mean = 0.0f;
for (const float v : w) mean += v;
mean /= static_cast<float>(w.size());
w.insert(w.begin() + std::min<int>(k, static_cast<int>(w.size())), mean);
}
rebuild_all();
return true;
}
bool RowSet::remove_cell(int plot) {
const int fr = figure_row_of(plot);
if (fr < 0) return false;
if (cells_per_row_[static_cast<std::size_t>(fr)] == 1) {
return remove_row(fr); // false when it is the last row
}
for (int i = plot; i + 1 < rows_; ++i) carry_row(row(i), row(i + 1));
--rows_;
clear_row(row(rows_));
const int k = plot - first_plot_of_row(fr);
--cells_per_row_[static_cast<std::size_t>(fr)];
auto& w = cell_weights_[static_cast<std::size_t>(fr)];
if (cells_per_row_[static_cast<std::size_t>(fr)] < 2) {
w.clear();
} else if (k < static_cast<int>(w.size())) {
w.erase(w.begin() + k);
}
rebuild_all();
return true;
}
void RowSet::set_cell_weights(int fr, std::vector<float> w) {
if (fr < 0 || fr >= figure_row_count()) return;
cell_weights_[static_cast<std::size_t>(fr)] = std::move(w);
if (figure_ != nullptr) apply_cell_weights();
}
void RowSet::set_cells_per_row(std::vector<int> cells) {
std::vector<int> next;
int total = 0;
for (int c : cells) {
c = std::clamp(c, 1, kMaxCellsPerRow);
if (total + c > kMaxRows) c = kMaxRows - total;
if (c < 1) break;
next.push_back(c);
total += c;
}
if (next.empty()) {
next.push_back(1);
total = 1;
}
cells_per_row_ = std::move(next);
cell_weights_.assign(cells_per_row_.size(), {});
rows_ = total;
rebuild_all();
}
void RowSet::install(Row& r, int /*row_index*/, plot::Plot& p) {
r.plot = &p;
r.axis_x = r.plot->add_x_axis(
@@ -302,14 +431,36 @@ void RowSet::set_row_weights(std::vector<float> weights) {
void RowSet::apply_row_weights() {
if (row_weights_.empty() || figure_ == nullptr) return;
row_weights_.resize(static_cast<std::size_t>(rows_), 1.0f);
row_weights_.resize(static_cast<std::size_t>(figure_row_count()), 1.0f);
(void)figure_->set_row_weights(row_weights_);
}
void RowSet::apply_cell_weights() {
if (figure_ == nullptr) return;
cell_weights_.resize(cells_per_row_.size());
for (int fr = 0; fr < figure_row_count(); ++fr) {
auto& w = cell_weights_[static_cast<std::size_t>(fr)];
const int n = cells_per_row_[static_cast<std::size_t>(fr)];
if (n < 2) {
w.clear();
continue;
}
if (w.empty()) {
figure_->clear_cell_weights(static_cast<plot::u32>(fr));
continue;
}
w.resize(static_cast<std::size_t>(n), 1.0f);
(void)figure_->set_cell_weights(static_cast<plot::u32>(fr), w);
}
}
void RowSet::rebuild_all() {
figure_ = std::make_unique<plot::Figure>(static_cast<plot::u32>(rows_));
const std::vector<plot::u32> cells(cells_per_row_.begin(), cells_per_row_.end());
figure_ = std::make_unique<plot::Figure>(std::span<const plot::u32>(cells));
figure_->set_row_gap(kRowGap);
figure_->set_col_gap(kColGap);
apply_row_weights();
apply_cell_weights();
x_group_ = figure_->add_x_link();
for (int i = 0; i < kMaxRows; ++i) {
Row& r = row(i);
@@ -917,7 +1068,7 @@ bool RowSet::update_time_labels() {
if (!relative_x_) r.plot->set_x_axis_origin(r.axis_x, 0.0);
// The caption belongs with the labels, which a figure draws once, on the bottom row (the
// one whose x axis is the figure's). Every other row gets an empty one, which draws nothing.
const bool bottom = i == rows_ - 1;
const bool bottom = i >= first_plot_of_row(figure_row_count() - 1);
if (!relative_x_ || !bottom) {
r.plot->set_x_note("");
continue;
+51 -8
View File
@@ -155,8 +155,43 @@ class RowSet {
double ruler_b_y = 0.0;
};
/// The number of **plots** (cells over all figure rows), which is what `row(i)` indexes. With no
/// row split it is also the number of figure rows; `figure_row_count()` is the latter.
int row_count() const noexcept { return rows_; }
/// A figure row holds one to `kMaxCellsPerRow` plots side by side, sharing the row's height and
/// the figure's one x window. The flattened plot index (`row(i)`, `Row::plot`, the figure's
/// `plot(i)`) runs row-major: row 0's cells left to right, then row 1's.
static constexpr int kMaxCellsPerRow = 4;
int figure_row_count() const noexcept { return static_cast<int>(cells_per_row_.size()); }
/// How many cells figure row `fr` holds; 0 for a row that does not exist.
int cells_in_row(int fr) const;
/// The flattened index of figure row `fr`'s first cell; `row_count()` for `fr ==
/// figure_row_count()`.
int first_plot_of_row(int fr) const;
/// The figure row plot `plot` is in, or -1 for an index that is not a plot.
int figure_row_of(int plot) const;
/// Plot `plot`'s 0-based position inside its row, or -1.
int cell_of(int plot) const;
/// Figure row `fr`'s cell width shares (empty means equal; always empty for a single-cell row).
const std::vector<float>& cell_weights(int fr) const;
/// What the UI calls a plot: the figure row's number (`"2"`) for a single-cell row, the number
/// and a letter (`"2a"`..`"2d"`) for a split one.
std::string plot_label(int plot) const;
/// Adds an empty cell to figure row `fr` at position `at` (clamped to the row), pushing the
/// cells at and after it right. False at `kMaxCellsPerRow` cells or `kMaxRows` plots.
bool insert_cell(int fr, int at);
/// Removes plot `plot`. A single-cell row removes the row itself (`remove_row()`); false when
/// that would leave no plot.
bool remove_cell(int plot);
/// Figure row `fr`'s cell width shares, normalised by the plotter; empty means equal.
void set_cell_weights(int fr, std::vector<float> w);
/// Replaces the whole arrangement: `cells[r]` plots in figure row `r`, each clamped to
/// `1..kMaxCellsPerRow` and the total to `kMaxRows`. Weights are reset; plot contents stay in
/// their flattened slots.
void set_cells_per_row(std::vector<int> cells);
/// The alpha every trace is drawn with. The *group* views use it: accumulating events wants their
/// traces translucent so that agreement between them reads as brightness, which is the whole point
/// of a persistence display. It is mixed into the palette colour where the trace is created, so it
@@ -169,22 +204,24 @@ class RowSet {
/// trace set or the row count changes, so weights that lived only in the figure would be lost on the
/// first drop or row change after a user had sized their layout.
void set_row_weights(std::vector<float> weights);
/// Sizes the stored weights to the rows that exist and hands them to the figure.
/// Sizes the stored weights to the figure rows that exist and hands them to the figure. Weights
/// are per **figure row**, not per plot.
void apply_row_weights();
const std::vector<float>& row_weights() const noexcept { return row_weights_; }
/// Shows `rows` rows. A changed count rebuilds the figure, since a `Figure`'s row
/// count is fixed at construction.
/// Shows `rows` figure rows of one cell each (so `rows` plots, any split undone). A changed
/// arrangement rebuilds the figure, since a `Figure`'s shape is fixed at construction.
void set_row_count(int rows);
/// Inserts an empty row at `at` (0 = above the first row, `row_count()` = below the
/// last), pushing the rows at and below it down. Returns false at `kMaxRows`.
/// Inserts an empty single-cell figure row at `at` (0 = above the first row,
/// `figure_row_count()` = below the last), pushing the rows at and below it down with their
/// cells. Returns false at `kMaxRows` plots.
///
/// The rows below move as *rows*: their trace assignments, colours and cursors follow
/// them, so inserting above row 1 does not renumber what row 1 was plotting. The
/// figure's `Plot`s are not what moves -- they are rebuilt and re-dealt -- which is
/// the same mechanism a row-count change already used.
bool insert_row(int at);
/// Removes row `at` and pulls the rows below it up. Returns false when only one row
/// is left: a figure with no rows has nothing to draw and no way back.
/// Removes figure row `at` (with all its cells) and pulls the rows below it up. Returns false
/// when only one row is left: a figure with no rows has nothing to draw and no way back.
bool remove_row(int at);
Row& row(int i) { return storage_[static_cast<std::size_t>(i)]; }
const Row& row(int i) const { return storage_[static_cast<std::size_t>(i)]; }
@@ -563,9 +600,15 @@ class RowSet {
std::unique_ptr<plot::Figure> figure_;
/// The one x link group every visible row belongs to. Groups are numbered from 1.
plot::u32 x_group_ = 0;
/// The number of plots; `sum(cells_per_row_) == rows_` always.
int rows_ = 1;
/// Cells per figure row, and each row's cell width shares (empty = equal; kept empty for a
/// single-cell row). Both are state of the `RowSet`, restated to every new figure.
std::vector<int> cells_per_row_{1};
std::vector<std::vector<float>> cell_weights_ = std::vector<std::vector<float>>(1);
void apply_cell_weights();
float trace_alpha_ = 1.0f;
std::vector<float> row_weights_;
std::vector<float> row_weights_; ///< per figure row
double window_s_ = 2.0;
double trigger_marker_x_ = 0.0;
bool trigger_marker_on_ = false;
+3
View File
@@ -6003,6 +6003,9 @@ int run_ui(int argc, char** argv, const Config& cfg, ScopeModel& model, Acquisit
if (const char* env = g_getenv("UDPSCOPE_PROBE_LAYOUT_MS")) {
g_timeout_add(static_cast<guint>(std::atoi(env)), layout_probe_step, &app);
}
if (const char* env = g_getenv("UDPSCOPE_PROBE_SPLIT_MS")) {
g_timeout_add(static_cast<guint>(std::atoi(env)), split_probe_step, &app);
}
if (const char* env = g_getenv("UDPSCOPE_TRIG_TRACE_MS")) {
g_timeout_add(static_cast<guint>(std::atoi(env)), trig_trace_probe, &app);
}
+5
View File
@@ -221,6 +221,8 @@ struct App {
/// a frame apart, because the buttons are placed from the rects the last frame was laid
/// out with, so the sequence cannot be one function.
int dbg_layout_step = 0;
/// Which step of the split probe runs next (`UDPSCOPE_PROBE_SPLIT_MS`).
int dbg_split_step = 0;
/// Which step of the cursors probe runs next (`UDPSCOPE_PROBE_CURSORS_MS`).
int dbg_cursors_step = 0;
/// Which step of the fit probe runs next (`UDPSCOPE_PROBE_FIT_MS`).
@@ -2095,6 +2097,9 @@ void layout_probe_report(App& a, const char* what);
void layout_click(GtkWidget* b);
void layout_probe_next(App& a, int step, guint delay_ms);
gboolean layout_probe_step(gpointer user);
void split_probe_report(App& a, const char* tag);
void split_probe_next(App& a);
gboolean split_probe_step(gpointer user);
gboolean enable_all_probe(gpointer user);
gboolean disarm_probe_step2(gpointer user);
gboolean disarm_probe_step3(gpointer user);
+213
View File
@@ -3796,6 +3796,219 @@ gboolean layout_probe_step(gpointer user) {
return G_SOURCE_REMOVE;
}
// --- the horizontal plot split (`UDPSCOPE_PROBE_SPLIT_MS`) -------------------------------------
//
// Each step changes the arrangement of cells, re-points the area at the new figure, and lets the
// next step (250 ms on) read what a frame was actually laid out with.
void split_probe_report(App& a, const char* tag) {
RowSet& rs = *a.rows;
const double scale = (a.area != nullptr && a.area->device_scale() > 0.0f)
? static_cast<double>(a.area->device_scale()) : 1.0;
const plot::FigureLayout& fl = rs.figure().layout();
const int frows = rs.figure_row_count();
// tile: every row's cells fill its rect edge to edge, share its y and height, and are one
// `col_gap` apart.
int tile = fl.cells.size() == static_cast<std::size_t>(rs.row_count()) &&
fl.rows.size() == static_cast<std::size_t>(frows)
? 1 : 0;
std::string cells_s;
std::string widths_s;
std::string weights_s;
for (int fr = 0; fr < frows && tile; ++fr) {
const int first = rs.first_plot_of_row(fr);
const int n = rs.cells_in_row(fr);
const plot::Rect& row = fl.rows[static_cast<std::size_t>(fr)];
const plot::Rect& c0 = fl.cells[static_cast<std::size_t>(first)];
const plot::Rect& cn = fl.cells[static_cast<std::size_t>(first + n - 1)];
if (std::fabs(c0.x - row.x) > 0.5f) tile = 0;
if (std::fabs(cn.right() - row.right()) > 0.5f) tile = 0;
for (int k = 0; k < n; ++k) {
const plot::Rect& c = fl.cells[static_cast<std::size_t>(first + k)];
if (std::fabs(c.y - row.y) > 0.5f || std::fabs(c.h - row.h) > 0.5f) tile = 0;
if (k > 0) {
const plot::Rect& prev = fl.cells[static_cast<std::size_t>(first + k - 1)];
if (std::fabs((c.x - prev.right()) - fl.col_gap) > 0.5f) tile = 0;
}
}
}
for (int fr = 0; fr < frows; ++fr) {
const int first = rs.first_plot_of_row(fr);
const int n = rs.cells_in_row(fr);
cells_s += (fr == 0 ? "" : ",") + std::to_string(n);
widths_s += fr == 0 ? "" : "|";
weights_s += fr == 0 ? "" : "|";
for (int k = 0; k < n; ++k) {
char buf[32];
const std::size_t idx = static_cast<std::size_t>(first + k);
std::snprintf(buf, sizeof(buf), "%s%.0f", k == 0 ? "" : ",",
idx < fl.cells.size() ? static_cast<double>(fl.cells[idx].w) / scale : -1.0);
widths_s += buf;
}
const std::vector<float>& w = rs.cell_weights(fr);
if (w.empty()) {
weights_s += "-";
} else {
for (std::size_t k = 0; k < w.size(); ++k) {
char buf[32];
std::snprintf(buf, sizeof(buf), "%s%g", k == 0 ? "" : ",", static_cast<double>(w[k]));
weights_s += buf;
}
}
}
// x_equal: every plot's x range is plot 0's. cursors_aligned: every plot's two vertical cursors
// sit where plot 0's do.
int x_equal = 1;
int aligned = 1;
const RowSet::Row& r0 = rs.row(0);
if (r0.plot != nullptr) {
const plot::Range ref = r0.plot->x_range(r0.axis_x);
const double ref_a = r0.plot->cursor(r0.cursor_a).x;
const double ref_b = r0.plot->cursor(r0.cursor_b).x;
const double span = std::max(1e-12, std::fabs(ref.max - ref.min));
for (int p = 1; p < rs.row_count(); ++p) {
const RowSet::Row& r = rs.row(p);
if (r.plot == nullptr) { x_equal = 0; aligned = 0; continue; }
const plot::Range x = r.plot->x_range(r.axis_x);
if (std::fabs(x.min - ref.min) > 1e-9 * span || std::fabs(x.max - ref.max) > 1e-9 * span) {
x_equal = 0;
}
if (std::fabs(r.plot->cursor(r.cursor_a).x - ref_a) > 1e-9 * span) aligned = 0;
if (std::fabs(r.plot->cursor(r.cursor_b).x - ref_b) > 1e-9 * span) aligned = 0;
}
}
std::string traces;
for (int p = 0; p < rs.row_count(); ++p) {
traces += p == 0 ? "" : "|";
traces += rs.plot_label(p) + ":";
bool any = false;
const RowSet::Row& row = rs.row(p);
for (int s = 0; s < RowSet::kSlotsPerRow; ++s) {
const std::string& n = row.signal_name[static_cast<std::size_t>(s)];
if (n.empty()) continue;
traces += (any ? "," : "") + n;
any = true;
}
if (!any) traces += "-";
}
std::fprintf(stderr,
"[split-probe] %-14s plots=%d frows=%d cells=[%s] widths=[%s] tile=%d x_equal=%d "
"cursors_aligned=%d weights=[%s] traces=[%s]\n",
tag, rs.row_count(), frows, cells_s.c_str(), widths_s.c_str(), tile, x_equal,
aligned, weights_s.c_str(), traces.c_str());
std::fflush(stderr);
}
namespace {
/// After any structural change: the new figure is the area's, and the frame tick re-lays it out.
void split_probe_restructured(App& a) {
if (a.area != nullptr) a.area->set_figure(&a.rows->figure());
a.relayout_pending = true;
}
/// Channel `c` onto plot `p` (the first free slot), by the same name/colour route a drop takes.
void split_probe_assign(App& a, int p, std::size_t c) {
if (c >= a.model->channel_count() || p < 0 || p >= a.rows->row_count()) return;
const ChannelInfo& ch = a.model->channels()[c];
a.rows->assign(p, 0, ch.name, ch.name, signal_color_index(a.model->signals(), ch.signal));
}
void split_probe_clear_all(App& a) {
for (int r = 0; r < RowSet::kMaxRows; ++r) {
for (int s = 0; s < RowSet::kSlotsPerRow; ++s) a.rows->assign(r, s, "", "", 0);
}
}
} // namespace
void split_probe_next(App& a) {
++a.dbg_split_step;
g_timeout_add(250, split_probe_step, &a);
}
gboolean split_probe_step(gpointer user) {
App* a = static_cast<App*>(user);
if (a->window == nullptr || a->rows == nullptr) return G_SOURCE_REMOVE;
RowSet& rs = *a->rows;
switch (a->dbg_split_step) {
case 0: {
rs.set_cells_per_row({1});
split_probe_clear_all(*a);
// The `enable_all_probe` assignment, in one call and onto plot 0's slots.
std::vector<std::size_t> all(a->model->signals().size());
for (std::size_t i = 0; i < all.size(); ++i) all[i] = i;
a->model->set_signals_enabled(all, true);
int slot = 0;
for (std::size_t c = 0; c < a->model->channel_count() && slot < RowSet::kSlotsPerRow; ++c) {
const ChannelInfo& ch = a->model->channels()[c];
rs.assign(0, slot++, ch.name, ch.name, signal_color_index(a->model->signals(), ch.signal));
}
split_probe_restructured(*a);
split_probe_next(*a);
break;
}
case 1:
split_probe_report(*a, "start");
rs.insert_cell(0, 1);
split_probe_clear_all(*a);
split_probe_assign(*a, 0, 0);
split_probe_assign(*a, 1, 1);
rs.set_cursors_visible(true);
rs.reset_cursors();
split_probe_restructured(*a);
split_probe_next(*a);
break;
case 2:
split_probe_report(*a, "split");
rs.set_cell_weights(0, {3.0f, 1.0f});
split_probe_restructured(*a);
split_probe_next(*a);
break;
case 3:
split_probe_report(*a, "weights 3:1");
rs.insert_row(1);
rs.insert_cell(1, 1);
split_probe_restructured(*a);
split_probe_next(*a);
break;
case 4: {
split_probe_report(*a, "row+cell");
// Fill row 0 to its cap, then keep adding rows to the plot cap; the first refusal of each kind
// is what is being read.
std::string cell_calls;
int refused_cell = 0;
for (int i = 0; i < RowSet::kMaxCellsPerRow + 1; ++i) {
const bool ok = rs.insert_cell(0, rs.cells_in_row(0));
cell_calls += (i == 0 ? "" : ",") + std::to_string(ok ? 1 : 0);
if (!ok) { ++refused_cell; break; }
}
std::string row_calls;
int refused_row = 0;
for (int i = 0; i < RowSet::kMaxRows; ++i) {
const bool ok = rs.insert_row(rs.figure_row_count());
row_calls += (i == 0 ? "" : ",") + std::to_string(ok ? 1 : 0);
if (!ok) { ++refused_row; break; }
}
std::fprintf(stderr,
"[split-probe] caps-calls insert_cell=[%s] refused_cell=%d insert_row=[%s] "
"refused_row=%d plots=%d\n",
cell_calls.c_str(), refused_cell, row_calls.c_str(), refused_row, rs.row_count());
split_probe_restructured(*a);
split_probe_next(*a);
break;
}
case 5:
split_probe_report(*a, "caps");
rs.remove_cell(rs.first_plot_of_row(0) + rs.cells_in_row(0) - 1);
split_probe_restructured(*a);
split_probe_next(*a);
break;
default:
split_probe_report(*a, "remove");
break;
}
return G_SOURCE_REMOVE;
}
gboolean enable_all_probe(gpointer user) {
App* a = static_cast<App*>(user);
// In one call, the way a restored session does it: enabling them one at a time re-plans