@@ -2585,17 +2585,11 @@ GMTVTK_API int gmtvtk_mb_tag_cloud_grid_h(void *handle, const char *name) {
25852585// A line area's "Show point-cloud": the window's swath soundings inside the area, in a NEW point-cloud
25862586// window (gmtvtk_view_points, the window every point cloud gets) with its own 3D Soundings pane
25872587// (mb3dsdgOpenAreaCloud). Closing that window is its pane's Discard.
2588- static void mbShowAreaCloud(Scene *s, const std::vector<std::array<double, 3>> &ring) {
2589- if (!sceneAlive(s) || ring.size() < 4)
2590- return;
2591- std::vector<double> xy;
2592- const size_t n = (ring.front() == ring.back()) ? ring.size() - 1 : ring.size(); // the closing duplicate
2593- for (size_t i = 0; i < n; i++) {
2594- xy.push_back(ring[i][0]);
2595- xy.push_back(ring[i][1]);
2596- }
2588+ // The window a sub-cloud view (an area's, a track's) opens in: gmtvtk_view_points, the window every
2589+ // point cloud gets, framed on the soundings it is given. ONE maker and ONE closer for both views.
2590+ static std::function<void *(const double *, int, const QString &)> mbSubCloudWindowMaker(Scene *s) {
25972591 const int geog = s->baseGeog ? 1 : 0;
2598- auto makeWindow = [geog](const double *xyz, int np, const QString &title) -> void * {
2592+ return [geog](const double *xyz, int np, const QString &title) -> void * {
25992593 double x0 = xyz[0], x1 = xyz[0], y0 = xyz[1], y1 = xyz[1];
26002594 for (int i = 1; i < np; i++) {
26012595 x0 = std::min(x0, xyz[3 * i]); x1 = std::max(x1, xyz[3 * i]);
@@ -2604,11 +2598,40 @@ static void mbShowAreaCloud(Scene *s, const std::vector<std::array<double, 3>> &
26042598 return gmtvtk_view_points(xyz, np, nullptr, nullptr, 0, x0, x1, y0, y1, geog, 3.0, 1.0, 0.0, 0.0,
26052599 title.toUtf8().constData());
26062600 };
2607- auto closeWin = [](void *w) { if (sceneAlive(static_cast<Scene *>(w))) gmtvtk_close(w); };
2601+ }
2602+ static void mbSubCloudWindowClose(void *w) { if (sceneAlive(static_cast<Scene *>(w))) gmtvtk_close(w); }
2603+
2604+ static void mbShowAreaCloud(Scene *s, const std::vector<std::array<double, 3>> &ring) {
2605+ if (!sceneAlive(s) || ring.size() < 4)
2606+ return;
2607+ std::vector<double> xy;
2608+ const size_t n = (ring.front() == ring.back()) ? ring.size() - 1 : ring.size(); // the closing duplicate
2609+ for (size_t i = 0; i < n; i++) {
2610+ xy.push_back(ring[i][0]);
2611+ xy.push_back(ring[i][1]);
2612+ }
26082613 QApplication::setOverrideCursor(Qt::WaitCursor);
2609- mb3dsdgOpenAreaCloud(s, xy.data(), int(xy.size() / 2), makeWindow, closeWin );
2614+ mb3dsdgOpenAreaCloud(s, xy.data(), int(xy.size() / 2), mbSubCloudWindowMaker(s), mbSubCloudWindowClose );
26102615 QApplication::restoreOverrideCursor();
26112616}
2617+
2618+ // A navigation track's "Show point-cloud" (its "MB-System" submenu, addMbSystemMenu): the soundings of
2619+ // its swath file(s), in the same view an area gets. Accept / Discard / the parent's pane: the same.
2620+ static void mbShowFilesCloud(Scene *s, const QStringList &files) {
2621+ if (!sceneAlive(s) || files.isEmpty())
2622+ return;
2623+ QApplication::setOverrideCursor(Qt::WaitCursor);
2624+ mb3dsdgOpenFileCloud(s, files, mbSubCloudWindowMaker(s), mbSubCloudWindowClose);
2625+ QApplication::restoreOverrideCursor();
2626+ }
2627+ // The same from the host: window `handle`'s cloud, the soundings of the swath file `file`. 1 = opened
2628+ GMTVTK_API int gmtvtk_mb_file_cloud_h(void *handle, const char *file) {
2629+ Scene *s = static_cast<Scene *>(handle);
2630+ if (!sceneAlive(s) || !file || !*file)
2631+ return 0;
2632+ mbShowFilesCloud(s, QStringList{ QString::fromUtf8(file) });
2633+ return mb3dsdgAreaOpen() ? 1 : 0;
2634+ }
26122635// The same "Show point-cloud" from coordinates: the soundings of window `handle`'s swath cloud inside the
26132636// polygon of n x,y vertices (true coords). 1 = the area window and its pane opened
26142637GMTVTK_API int gmtvtk_mb_area_cloud_h(void *handle, const double *xy, int n) {
@@ -2626,6 +2649,11 @@ GMTVTK_API int gmtvtk_mb_area_cloud_h(void *handle, const double *xy, int n) {
26262649GMTVTK_API int gmtvtk_mb_soundings_counts(int *out) {
26272650 return (out && mb3dsdgCounts(&out[0], &out[1], &out[2])) ? 1 : 0;
26282651}
2652+ // The open 3D Soundings pane's buttons that are shown: bit 0 CUBE filter, bit 1 Gridding, bit 2 Discard
2653+ // (an area / track view). -1 = no pane
2654+ GMTVTK_API int gmtvtk_mb_pane_buttons() {
2655+ return mb3dsdgPaneButtons();
2656+ }
26292657// View > Show flagged of the open 3D Soundings view, through its menu entry. 1 = done
26302658GMTVTK_API int gmtvtk_mb_soundings_show_flagged(int on) {
26312659 return mb3dsdgSetShowFlagged(on != 0) ? 1 : 0;
@@ -3092,7 +3120,7 @@ GMTVTK_API int gmtvtk_mbgrdviz_track_menu_test(void *handle, const char *element
30923120 break;
30933121 }
30943122 QMenu m;
3095- addMbSystemMenu(m, navs, s->win, own);
3123+ addMbSystemMenu(m, navs, s->win, own, s );
30963124 std::string items;
30973125 int n = 0;
30983126 for (QAction *a : m.actions())
@@ -7402,10 +7430,16 @@ GMTVTK_API int gmtvtk_ecmwf_dialog_test(void *handle, const char *path) {
74027430// out here for a scene that is perfectly alive. The Scene struct is byte-identical between the two
74037431// dlls (same source, same compiler), so adopting the pointer is the same mirroring the callback
74047432// registrations already do.
7433+ static void testBorrowScene(Scene *s); // below: borrow, given back when the window goes
7434+ // Does THIS dll still count `scene` as a live window? Pointer comparison only -- safe to ask about a
7435+ // window that is already gone, which is the point: after its window closes the answer must be 0.
7436+ GMTVTK_API int gmtvtk_scene_borrowed_test(void *scene) {
7437+ return g_scenes.count(static_cast<Scene *>(scene)) ? 1 : 0;
7438+ }
74057439GMTVTK_API int gmtvtk_scene_adopt_test(void *scene) {
74067440 Scene *s = static_cast<Scene *>(scene);
74077441 if (!s) return 0;
7408- g_scenes.insert (s);
7442+ testBorrowScene (s);
74097443 return 1;
74107444}
74117445
@@ -8040,6 +8074,20 @@ GMTVTK_API void *gmtvtk_open_empty(const char *title); // defined below; forwa
80408074//
80418075// ONE function does the borrowing, for every hook, so no site can forget the giving-back half. A
80428076// repeat open on the same dialog just connects again — the handler is idempotent.
8077+ // The borrowing for a hook that has no dialog slot to tie it to (a scene adopted as it is, or a dialog
8078+ // kept in a per-window registry rather than a g_*TestDlg pointer): the borrowed Scene is given back when
8079+ // its WINDOW is destroyed. Without this a closed window stays "alive" here for ever, and the next thing
8080+ // in this dll that walks g_scenes -- rebuildSceneObjects -> swipeRefreshAvailability ->
8081+ // linkPartnerWindows -- reads the freed Scene: std::bad_alloc / SIGSEGV on Linux and macOS (2026-10-07,
8082+ // the grdlandmask parking test right after the DGT one), silently "fine" on Windows.
8083+ static void testBorrowScene(Scene *s) {
8084+ if (!s) return;
8085+ g_scenes.insert(s);
8086+ static std::unordered_set<Scene *> wired; // one give-back per window, however often borrowed
8087+ if (s->win && wired.insert(s).second)
8088+ QObject::connect(s->win, &QObject::destroyed, [s]() { g_scenes.erase(s); wired.erase(s); });
8089+ }
8090+
80438091template <class T>
80448092static void testBorrowWindow(Scene *s, T **slot) {
80458093 if (!s) return;
@@ -8672,7 +8720,7 @@ GMTVTK_API int gmtvtk_movie_open_dialog_test(void *handle) {
86728720 ensureApp();
86738721 Scene *s = static_cast<Scene *>(handle);
86748722 if (!s || !s->win) return 0;
8675- g_scenes.insert (s);
8723+ testBorrowScene (s);
86768724 return gmtvtk_open_movie_dialog_h(s);
86778725}
86788726// The X, driven the way a user drives it.
@@ -8712,7 +8760,7 @@ GMTVTK_API int gmtvtk_dgt_open_dialog_test(void *handle) {
87128760 ensureApp();
87138761 Scene *s = static_cast<Scene *>(handle);
87148762 if (!s || !s->win) return 0;
8715- g_scenes.insert (s);
8763+ testBorrowScene (s);
87168764 DgtLidarDialog *w = dgtTestDlg(handle);
87178765 if (w) w->unpark();
87188766 else {
@@ -8783,7 +8831,7 @@ GMTVTK_API int gmtvtk_landmask_open_dialog_test(void *handle) {
87838831 ensureApp();
87848832 Scene *s = static_cast<Scene *>(handle);
87858833 if (!s || !s->win) return 0;
8786- g_scenes.insert (s);
8834+ testBorrowScene (s);
87878835 const int ok = openGrdLandmaskDialog(s->win, s);
87888836 QApplication::processEvents();
87898837 return ok;
0 commit comments