ask which source to open when --server is absent
The chooser runs before ScopeModel exists, so declining costs no buffer and no socket. UDPSCOPE_PROBE_SOURCES=1 prints what the discovery found and exits, before gtk_init(), so the list is checkable with no display.
This commit is contained in:
1 parent
9ae059db1b
commit
0be3feecc5
6 files changed
+91
No files matched your search
@@ -63,6 +63,7 @@ add_executable(udpscope
|
|||||||
src/plot_host.cpp
|
src/plot_host.cpp
|
||||||
src/export.cpp
|
src/export.cpp
|
||||||
src/layout_store.cpp
|
src/layout_store.cpp
|
||||||
|
src/source_dialog.cpp
|
||||||
src/measure.cpp
|
src/measure.cpp
|
||||||
src/math_channel.cpp
|
src/math_channel.cpp
|
||||||
src/ui.cpp
|
src/ui.cpp
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ Config parse_args(int argc, char** argv) {
|
|||||||
std::exit(0);
|
std::exit(0);
|
||||||
} else if (a == "--server" || a.rfind("--server=", 0) == 0) {
|
} else if (a == "--server" || a.rfind("--server=", 0) == 0) {
|
||||||
c.server = value_of(argc, argv, i, "--server");
|
c.server = value_of(argc, argv, i, "--server");
|
||||||
|
c.server_specified = true;
|
||||||
} else if (a == "--port" || a.rfind("--port=", 0) == 0) {
|
} else if (a == "--port" || a.rfind("--port=", 0) == 0) {
|
||||||
const std::string v = value_of(argc, argv, i, "--port");
|
const std::string v = value_of(argc, argv, i, "--port");
|
||||||
const long p = std::strtol(v.c_str(), nullptr, 10);
|
const long p = std::strtol(v.c_str(), nullptr, 10);
|
||||||
|
|||||||
@@ -16,6 +16,11 @@ struct Config {
|
|||||||
std::string multicast; ///< Empty selects unicast.
|
std::string multicast; ///< Empty selects unicast.
|
||||||
std::string interface_addr; ///< Local IPv4 to join the group on; empty = default route.
|
std::string interface_addr; ///< Local IPv4 to join the group on; empty = default route.
|
||||||
|
|
||||||
|
/// Whether `--server` was given. Absent, the application asks which source to open rather
|
||||||
|
/// than silently taking the default above -- so this is "did the user choose", not "is
|
||||||
|
/// `server` valid". The default stays a usable value for every other path.
|
||||||
|
bool server_specified = false;
|
||||||
|
|
||||||
/// Size of *each* of the two sample buffers, in megabytes. The application
|
/// Size of *each* of the two sample buffers, in megabytes. The application
|
||||||
/// always allocates two identical buffers (spec item 1).
|
/// always allocates two identical buffers (spec item 1).
|
||||||
double buffer_mb = 200.0;
|
double buffer_mb = 200.0;
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include "config.hpp"
|
#include "config.hpp"
|
||||||
#include "model.hpp"
|
#include "model.hpp"
|
||||||
#include "plot_host.hpp"
|
#include "plot_host.hpp"
|
||||||
|
#include "source_dialog.hpp"
|
||||||
#include "ui.hpp"
|
#include "ui.hpp"
|
||||||
|
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
@@ -19,6 +20,10 @@ int main(int argc, char** argv) {
|
|||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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".
|
||||||
|
if (!cfg.server_specified && !udpscope::choose_source(cfg)) return 0;
|
||||||
|
|
||||||
udpscope::ScopeModel model(cfg);
|
udpscope::ScopeModel model(cfg);
|
||||||
udpscope::RowSet rows;
|
udpscope::RowSet rows;
|
||||||
rows.set_row_count(cfg.plots);
|
rows.set_row_count(cfg.plots);
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// SPDX-FileCopyrightText: 2026 Martino Ferrari
|
||||||
|
|
||||||
|
#include "source_dialog.hpp"
|
||||||
|
|
||||||
|
#include "layout_store.hpp"
|
||||||
|
|
||||||
|
#include <gtk/gtk.h>
|
||||||
|
|
||||||
|
#include <cstdio>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace udpscope {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
/// A chooser row's source becomes the connection to open. The interface comes along with the
|
||||||
|
/// group: it is the local address the join happens on, so storing one without the other would
|
||||||
|
/// reconnect to the right group on the wrong path.
|
||||||
|
[[maybe_unused]] void apply_source(Config& cfg, const KnownSource& src) {
|
||||||
|
cfg.server = src.host;
|
||||||
|
cfg.port = src.port;
|
||||||
|
cfg.multicast = src.multicast;
|
||||||
|
cfg.interface_addr = src.interface_addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// One line per known source on stderr, in the style of the probes in `ui.cpp`.
|
||||||
|
void report_sources(const std::vector<KnownSource>& known) {
|
||||||
|
std::fprintf(stderr, "[sources] %zu known:", known.size());
|
||||||
|
for (const KnownSource& s : known) {
|
||||||
|
std::fprintf(stderr, " %s:%u plots=%d signals=%d", s.host.c_str(), s.port, s.rows, s.signals);
|
||||||
|
if (!s.multicast.empty()) std::fprintf(stderr, " multicast=%s", s.multicast.c_str());
|
||||||
|
if (!s.interface_addr.empty()) {
|
||||||
|
std::fprintf(stderr, " interface=%s", s.interface_addr.c_str());
|
||||||
|
}
|
||||||
|
std::fprintf(stderr, " |");
|
||||||
|
}
|
||||||
|
std::fprintf(stderr, "\n");
|
||||||
|
std::fflush(stderr);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
bool choose_source(Config& cfg) {
|
||||||
|
const std::vector<KnownSource> known = known_sources();
|
||||||
|
|
||||||
|
// The discovery half, checkable with no display at all: this returns before `gtk_init()`, so
|
||||||
|
// it asserts what the list found without needing an X server or a GL context. False means the
|
||||||
|
// caller exits 0, which is what "list and exit" wants.
|
||||||
|
if (g_getenv("UDPSCOPE_PROBE_SOURCES") != nullptr) {
|
||||||
|
report_sources(known);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
gtk_init();
|
||||||
|
(void)cfg;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace udpscope
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// SPDX-FileCopyrightText: 2026 Martino Ferrari
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "config.hpp"
|
||||||
|
|
||||||
|
namespace udpscope {
|
||||||
|
|
||||||
|
/// Asks which data source to open: a list of the sources with a stored session, and a form for a
|
||||||
|
/// new one. Returns true with `cfg`'s connection fields filled in, or false if the user declined
|
||||||
|
/// -- in which case `cfg` is untouched and the caller should exit.
|
||||||
|
///
|
||||||
|
/// Runs *before* `ScopeModel` exists, so declining costs no buffer and no socket. It drives its
|
||||||
|
/// own `gtk_init()` and nested `GMainLoop`; `run_ui()`'s `GtkApplication` comes afterwards in the
|
||||||
|
/// same process, which works because `gtk_init()` is idempotent.
|
||||||
|
bool choose_source(Config& cfg);
|
||||||
|
|
||||||
|
} // namespace udpscope
|
||||||
Reference in new issue
Block a user