fix: the math editor's source dropdown no longer hands a GParamSpec to a value handler
notify::selected passes a GParamSpec* ahead of the user data and GtkSpinButton::value-changed does not, so on_math_param_changed read the pspec as its MathDialog* and faulted on the first field it touched -- which is to say on opening the math editor and picking a source. The probe missed it because filling the form sets the dropdown to the value it already holds, and GObject emits no notify for that: a channel sourcing channels().front() picks index 0 on a dropdown already at 0. math_probe now opens on a channel sourcing the *last* acquired signal, which does move the selection, and reads back the source the handler wrote into the node. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
219ae91fd8
commit
cd9657663a
1 file changed
+41
-3
+41
-3
@@ -4696,6 +4696,16 @@ void on_math_param_changed(GtkWidget*, gpointer user) {
|
|||||||
gtk_widget_queue_draw(d->canvas);
|
gtk_widget_queue_draw(d->canvas);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The same read, for the source dropdown, and it needs its own entry point for the reason
|
||||||
|
/// `on_trigger_drop_changed()` gives: `notify::<prop>` passes a `GParamSpec*` ahead of the user
|
||||||
|
/// data and `GtkSpinButton::value-changed` does not, so wiring the two-parameter reader above to
|
||||||
|
/// `notify::selected` made it read the pspec as its `MathDialog*`. That is not a latent hazard --
|
||||||
|
/// it segfaulted on the first field it touched, on the first change of the source dropdown, which
|
||||||
|
/// is to say on opening the math editor and picking a source.
|
||||||
|
void on_math_source_changed(GObject*, GParamSpec*, gpointer user) {
|
||||||
|
on_math_param_changed(nullptr, user);
|
||||||
|
}
|
||||||
|
|
||||||
/// The op changed: the new op may take a different number of inputs, so the ones it cannot use are
|
/// The op changed: the new op may take a different number of inputs, so the ones it cannot use are
|
||||||
/// cleared rather than left pointing at a node the graph would then evaluate from anyway.
|
/// cleared rather than left pointing at a node the graph would then evaluate from anyway.
|
||||||
void on_math_op_changed(GObject* object, GParamSpec*, gpointer user) {
|
void on_math_op_changed(GObject* object, GParamSpec*, gpointer user) {
|
||||||
@@ -5027,7 +5037,7 @@ void open_math_dialog(App& app, int edit) {
|
|||||||
gtk_box_append(GTK_BOX(form), op_row);
|
gtk_box_append(GTK_BOX(form), op_row);
|
||||||
|
|
||||||
d->source_drop = gtk_drop_down_new_from_strings(nullptr);
|
d->source_drop = gtk_drop_down_new_from_strings(nullptr);
|
||||||
g_signal_connect(d->source_drop, "notify::selected", G_CALLBACK(on_math_param_changed), d);
|
g_signal_connect(d->source_drop, "notify::selected", G_CALLBACK(on_math_source_changed), d);
|
||||||
gtk_box_append(GTK_BOX(form), math_row("channel", d->source_drop, &d->rows[0]));
|
gtk_box_append(GTK_BOX(form), math_row("channel", d->source_drop, &d->rows[0]));
|
||||||
|
|
||||||
d->gain = gtk_spin_button_new_with_range(-1e6, 1e6, 0.1);
|
d->gain = gtk_spin_button_new_with_range(-1e6, 1e6, 0.1);
|
||||||
@@ -8012,10 +8022,20 @@ gboolean math_probe(gpointer user) {
|
|||||||
App* a = static_cast<App*>(user);
|
App* a = static_cast<App*>(user);
|
||||||
if (a->window == nullptr) return G_SOURCE_REMOVE;
|
if (a->window == nullptr) return G_SOURCE_REMOVE;
|
||||||
std::string name;
|
std::string name;
|
||||||
|
std::string late_name;
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(a->model->mutex());
|
std::lock_guard<std::mutex> lock(a->model->mutex());
|
||||||
if (a->model->channels().empty()) return G_SOURCE_REMOVE;
|
if (a->model->channels().empty()) return G_SOURCE_REMOVE;
|
||||||
name = a->model->channels().front().name;
|
name = a->model->channels().front().name;
|
||||||
|
// The *last* acquired channel, and the probe needs it rather than merely liking it.
|
||||||
|
// Filling the editor's form calls `gtk_drop_down_set_selected()` on the source
|
||||||
|
// dropdown, and GObject does not emit `notify` when a property is set to the value it
|
||||||
|
// already holds -- so a channel sourcing `channels().front()` picks index 0 on a
|
||||||
|
// freshly built dropdown that is already at 0, the signal never fires, and the whole
|
||||||
|
// `notify::selected` path goes unexercised. That is exactly how a handler of the wrong
|
||||||
|
// shape on that signal survived every run of this probe while crashing the app on the
|
||||||
|
// first editor a user opened.
|
||||||
|
late_name = a->model->channels().back().name;
|
||||||
}
|
}
|
||||||
std::vector<MathChannel> channels;
|
std::vector<MathChannel> channels;
|
||||||
MathChannel gain;
|
MathChannel gain;
|
||||||
@@ -8030,7 +8050,7 @@ gboolean math_probe(gpointer user) {
|
|||||||
MathChannel lp;
|
MathChannel lp;
|
||||||
lp.name = "math lp";
|
lp.name = "math lp";
|
||||||
lp.enabled = true;
|
lp.enabled = true;
|
||||||
lp.nodes = {MathNode{MathOp::Source, name}, MathNode{MathOp::LowPass}};
|
lp.nodes = {MathNode{MathOp::Source, late_name}, MathNode{MathOp::LowPass}};
|
||||||
lp.nodes[1].in_a = 0;
|
lp.nodes[1].in_a = 0;
|
||||||
lp.nodes[1].fc = 20.0;
|
lp.nodes[1].fc = 20.0;
|
||||||
lp.output = 1;
|
lp.output = 1;
|
||||||
@@ -8040,7 +8060,8 @@ gboolean math_probe(gpointer user) {
|
|||||||
// directly left the two disagreeing, and the next pane edit then silently dropped its channels.
|
// directly left the two disagreeing, and the next pane edit then silently dropped its channels.
|
||||||
a->math = std::move(channels);
|
a->math = std::move(channels);
|
||||||
apply_math_channels(*a);
|
apply_math_channels(*a);
|
||||||
std::fprintf(stderr, "[math-probe] 2 math channels on '%s'\n", name.c_str());
|
std::fprintf(stderr, "[math-probe] 2 math channels on '%s' and '%s'\n", name.c_str(),
|
||||||
|
late_name.c_str());
|
||||||
// And open the editor on the first one: as much a construction crash test as a state check,
|
// And open the editor on the first one: as much a construction crash test as a state check,
|
||||||
// since a modal window is presented and its canvas, form and gestures are all built here.
|
// since a modal window is presented and its canvas, form and gestures are all built here.
|
||||||
// The pane's own `+`, which is the path the user takes: it must source an *acquired* channel.
|
// The pane's own `+`, which is the path the user takes: it must source an *acquired* channel.
|
||||||
@@ -8055,6 +8076,23 @@ gboolean math_probe(gpointer user) {
|
|||||||
}
|
}
|
||||||
open_math_dialog(*a, 0);
|
open_math_dialog(*a, 0);
|
||||||
std::fprintf(stderr, "[math-probe] editor opened\n");
|
std::fprintf(stderr, "[math-probe] editor opened\n");
|
||||||
|
// Opened on the channel sourcing the *last* acquired signal, which is the one open that moves
|
||||||
|
// the source dropdown off index 0 and so emits `notify::selected` -- see `late_name` above for
|
||||||
|
// why nothing else here does. The reading is the source the handler wrote back into the node:
|
||||||
|
// it must be `late_name` and not the dropdown's first entry, because a handler that reads the
|
||||||
|
// wrong argument writes through a `GParamSpec*` and the Gtk-CRITICAL baseline is what catches
|
||||||
|
// it when the dereference happens not to fault.
|
||||||
|
{
|
||||||
|
if (a->math_dialog != nullptr) gtk_window_close(GTK_WINDOW(a->math_dialog));
|
||||||
|
open_math_dialog(*a, 1);
|
||||||
|
auto* ed = static_cast<MathDialog*>(
|
||||||
|
g_object_get_data(G_OBJECT(a->math_dialog), "udpscope-math-dialog"));
|
||||||
|
const char* read = "NO DIALOG";
|
||||||
|
if (ed != nullptr && !ed->nodes.empty()) read = ed->nodes[0].source.c_str();
|
||||||
|
std::fprintf(stderr, "[math-probe] source drop: node reads '%s' (want '%s')\n", read,
|
||||||
|
late_name.c_str());
|
||||||
|
if (a->math_dialog != nullptr) gtk_window_close(GTK_WINDOW(a->math_dialog));
|
||||||
|
}
|
||||||
// The user's own flow, through the app's handlers: the pane's `+` makes a bare source graph, the
|
// The user's own flow, through the app's handlers: the pane's `+` makes a bare source graph, the
|
||||||
// editor adds a gain, the gain is set, Apply saves. The *output* must be the node that was just
|
// editor adds a gain, the gain is set, Apply saves. The *output* must be the node that was just
|
||||||
// added -- it defaulted to node 0, the source, so every operation was computed and discarded.
|
// added -- it defaulted to node 0, the source, so every operation was computed and discarded.
|
||||||
|
|||||||
Reference in new issue
Block a user