github wonderingStars/foxsdr v0.99.77
FoxSDR 0.99.77

4 hours ago

FoxSDR 0.99.77 lets you set the range of the spectrum and the waterfall yourself by right-clicking either one, groups the Fitted modules list and the DECODE bank's plugin list under the store's headings, opens a plugin's own window when you double-click its row in Fitted modules, lets a row in a plugin's panel be a key (press HD2 in the IBOC Radio panel, among HD1 to HD8, to choose that programme), and no longer dies when a window is dropped into, or pulled out of, a dock node. A user on 0.99.75 found the spectrum and the waterfall very milky; the menu has Range: Automatic or Manual, Floor and Ceiling sliders and Reset display to defaults, and after four seconds of rest over either picture a tip says the menu is there. A configuration that already had a range of its own opens in Manual with it. The whole of a store card, not only its text, now opens the plugin's page, and the key rows need IBOC Radio (nrsc5 library) 0.1.1 or newer. On Linux, a scan helper that loses its pipe is no longer filed as a crash and no longer takes a working radio's driver out of the session's scans, and 41 of the 43 catalogue plugins now have Linux x64 and arm64 builds, carried by the plugin store and not by this download. An old copy of a renamed plugin no longer loads beside the new one. This release carries the changes of 0.99.76, which was built and not published. The eight new interface words are in all 33 languages; the plugin ABI is still 3 and gains two definitions.


0.99.76 (commit 11dae30) was built and its CI passed but it was not published; its changes ship here.

SHA-256 of foxsdr-setup-0.99.77.exe:
8c175b2cae03d3467a762cd1c885e03ba6c23362c0bac3e9524defea24dddf80

SHA-256 of FoxSDR-0.99.77-x86_64.AppImage:
6537829a3d821226f71dc6f0179de66203211c145a2b86c66e30b4e4119a107d

SHA-256 of foxsdr-0.99.77-linux-x64.tar.gz:
5c9152f3c2c03486dd1397ed301ec76e218b50d13310ae425b00641aaa79c41d

SHA-256 of FoxSDR-0.99.77-aarch64.AppImage:
be9642d2938288e28ea0e6e314b51f68121d3f89c90a35f041f266ee5ef9b383

SHA-256 of foxsdr-0.99.77-linux-arm64.tar.gz:
aa1050b8ca6f2e0f2a2c8afd96e5330871350f98d45e7ec67a95dcf3afe3b671

Features and fixes, 2026-10-13

Six changes were made on 2026-10-13, after 0.99.75 was published: the display-range menu, the guard for the page grip's resize-corner id, the reading of a Linux scan helper's closed pipe, the grouping of the plugin lists with the two clicks beside it, the pressable panel row, and the handling of an old copy of a plugin whose name changed between versions. The first three were built together as 0.99.76 and its CI passed, but it was not published; the other three came after it, and 0.99.77 is the first published release with any of the six. The first answers a dislike from the field, the second a fatal crash report, the third two crash groups from one Linux session, the fourth and fifth answer US testers, relayed by the owner, and the sixth a US tester's screenshot of two TETRA sections and an ImGui error box, sent the evening the release was being built. The first three touch different parts of src/gui/app_window.cpp and src/source/soapy_enum_proc.* and applied together without a conflict. The grouping and the pressable row touch src/gui/app_window.cpp and src/gui/plugins_view.cpp in different places and applied together with one overlap: each had added the census line panel:window:<plugin> to the panel window's drawing, and it was kept once, with the second change's panel:rows:<plugin>:<n> beside it.

For the crash admin: type 0.99.77 as the fix release, and no earlier number, for these three groups. The fatal crash group crash cascade.exe @ ImGui::GetWindowResizeCornerID (fixed in 0.99.77) holds report D94CCD4584724FAC (0.99.58) and report DE3F72AAE6B69997 (0.99.75); it was marked fixed on 2026-10-01 with no release named, and the second report is the one that reopened it. The Linux enumeration groups are 862234B08B9FC33D and 30BCD594C128766D (0.99.73, six scan-helper deaths in one second from one session; both fixed in 0.99.77). 0.99.77 is the first published release that contains either change (0.99.76 contained both and was not published), so a report of one of these signatures from 0.99.75 or earlier is not a regression and one from 0.99.77 or later is. The Linux half has not been run on Linux (below), so a 0.99.77 report from a Linux machine is the first real test of it.

Right-click the spectrum or the waterfall to set the range of the picture yourself: the menu now ends with a Display block - Range: Automatic or Manual, a Floor and a Ceiling slider, and Reset display to defaults - and the strength key says MANUAL while your own window is in force; hold the pointer still over either panel for four seconds and a tip says that the right-click menu is there. A French user on 0.99.75 with an RTL-SDR V4 wrote that "the spectrum and the waterfall are very milky, one cannot see the signals", and the owner's answer was to let people set it themselves, by right-clicking. The window was always adjustable - SYSTEM > Display > Min dB and Max dB - but that section has been folded until opened since 0.99.74, and nobody with a milky picture had a reason to open it. The two sliders in the menu are the same two numbers, so either place changes both, and nothing in the signal path changed. The menu is on the receiver's waterfall (the marker menu), on its spectrum (the trace menu) and on a patch Spectrum part's picture and trace, the same block each time, after the items that were already there.

What Automatic and Manual are, because the two views meant different things by "automatic" before there was a word for it: the receiver's spectrum and waterfall have always been drawn through one fixed window, -110 to 0 dB, unless the sliders moved it, and that window is Automatic; a patch Spectrum part fits its own window to every line it draws (the 20th-percentile bin, which is the noise, up to the loudest bin, never under 30 dB), and that is Automatic there. Manual is your floor and your ceiling on all of them together, so the spectrum's scale, the waterfall's colours, the strength key and every patch Spectrum part agree about what a colour means. The ceiling stays at least 10 dB above the floor, and the slider you are dragging is the one that stops. Reset display to defaults goes back to Automatic, forgets the pair, and returns the spectrum to its normal trace. The choice is saved as waterfallRangeMode ("auto" or "manual") beside the dbMin and dbMax that have always been in config.json (the floor and ceiling Manual uses, kept while Automatic is in force); a file with no mode, from 0.99.75 or earlier, opens in Manual if its pair is anything but -110 and 0 and in Automatic if it is, so nobody's picture changes. The waterfall keeps the colours it has already painted, as it always has when the sliders moved: after a change the key says "APPLIES TO" the newest lines and a "RANGE CHANGED" rule crosses the picture. Display > Min dB and Max dB now show the window in force, make the range Manual when dragged, and clamp a typed value (ctrl-click entry could exceed the slider's bounds before).

The tip is the owner's: "when the mouse is over it for 4 secs". After four seconds of rest over the receiver's spectrum or waterfall, or a patch Spectrum part's trace or picture, a tooltip reads "Right-click to change the range and display", in place of the panel's usual gesture tip while the hand stays still. Moving more than 6 pixels, pressing a button, dragging the band, panning, opening the menu or leaving the panel puts it away, and it comes back after another full four seconds. One related fix: a range exactly 10 dB apart is now kept. The sliders always allowed it and the config loader refused it, so a range left there was thrown away, both ends back to -110 and 0, at the next start. The remote control's own range check still refuses a request that names both ends exactly 10 dB apart.

Verified on Windows with the real application. test_display_range (83 checks) pins the arithmetic: a sweep of 52,140 combinations of a pair and a moved end comes out in bounds and 10 dB apart every time, with the end that was not moved never touched; Automatic ignores the pair and Manual uses it; Reset forgets it; and a file's mode is read from its pair when it has no word. test_hover_dwell (133) pins the clock: not before 4.0 s, due at 4.0 s, a move, a press, a menu, leaving, ten seconds unasked and a bad clock each end the rest, and the spectrum's "not over me" does not undo the waterfall's. test_config (3,529) round-trips the new key, reads it from a pair when it is missing, wrong-typed or unknown, and sees it in configsEqual; its one old expectation, that a pair exactly 10 dB apart is reset, was wrong for the reason above and now expects the pair kept, with a new case a hair under 10 dB that is still refused. test_display_menu_app (108 checks, 23 runs of the real cascade.exe, 95 s) right-clicks the waterfall, the spectrum and a patch Spectrum part with the application's own input script and reads the UI census and the saved config: picking Manual and dragging Floor and Ceiling to 70% gave -59 / -14 in the config, on the strength key (with MANUAL) and on the spectrum's scale, all three the same two numbers; a ceiling dragged to 5% from -60 / -20 stopped at -50, and the floor then dragged to 95% stopped at -60; Reset from Manual with Peak hold gave Automatic, -110 / 0 and the normal trace; a restart in Manual showed -60 / -20 with MANUAL; the tip was not shown after 3.12 s of rest and was after 5.13 s, on the spectrum and on the waterfall; a right-click with the tip up opened the menu and took it away; a move left 1.64 s of rest and a press 1.20 s, not the old 5. Against the unchanged build the same test failed 34 of 70 checks (the steps that aim at the menu's items are skipped when the menu is absent), and deliberate breakages made the test named after each fail: fourteen one at a time in the arithmetic, the clock and the config, and three groups in the real application (the key's MANUAL, the spectrum's scale, the trace reset, the saved mode and the patch part's Manual removed together made 17 checks fail, the held button, the popup, the "over" test and the movement rule removed together 5, and the dwell cut to 0.5 s alone 7). The new words were seen by eye in screenshots of the real window, in English: the menu in Manual with the sliders at -59 / -14, the key reading "STRENGTH KEY - dB MANUAL", the menu in Automatic with its sliders greyed, and the tip. The right-click menu on the waterfall is now about 314 pixels tall, so low in the window it opens above the pointer; test_freq_markers_app clicked low in a patch part and was moved up to 5% down the picture for that reason, with its assertions unchanged.

Not fixed, and known: nobody has seen the reporter's screen, so whether a Manual floor cures that picture is a hypothesis. There was no screenshot and no bundle with the dislike, and the same reporter's 0.99.72 bundle logs seven theme changes in 43 seconds; Daylight Lab's colour ramp runs from a light colour at the floor to a dark one at the top, which looks milky by construction, and there is no waterfall palette to offer beside the range. No gamma or contrast control was added (the dB-to-colour map is a straight line into the theme's ramp and a gamma would reach addLine's three callers and the strength key's own scale), and the spectrum's and waterfall's existing gesture tips still say nothing about the range, which the four-second tip now does. Nothing was tried on Linux or Android, in a theme other than "today", at an interface scale other than 100%, or with a real radio, the patch Spectrum part's menu was not looked at by eye, and the 6-pixel slop and the 10-second gap are choices, not measurements.

FoxSDR no longer dies when a window is dropped into, or pulled out of, a dock node: the fatal GetWindowResizeCornerID crash reported on 0.99.58 and 0.99.75 is guarded. The page grip in AppWindow::beginPage asked ImGui for its resize corner's id in every frame, and ImGui answers that by reading the dock node's host window, which does not exist yet in the frames after a window is dropped on another one (or is still being created for it). Report DE3F72AAE6B69997 (0.99.75, from a machine that had opened the WEFAX window five minutes earlier, and probably the one whose user filed a bug that day saying that inserting a window at the bottom with the mouse does not work) faulted at GetWindowResizeCornerID+0x1b, which in a build of the same source is the load of the host window's id; the 0.99.58 report D94CCD4584724FAC has the same two frames. The id is now asked of one helper, gui/page_resize_id.hpp, that answers 0 when there is no window or no host to ask, and a lit grip is no longer decided by 0 meeting 0 (the first version of the guard would have lit the grip and turned the cursor into a resize arrow in every frame that nothing was hovered). A new test, test_page_resize_id (22 checks), builds the windows ImGui builds in those frames; before the guard it faulted with 0xC0000005 on the null window, on a docked window with no node and on a docked window whose node has no host, and with the guard it passes; the guard was removed in four ways and each turned a named check red. It also scans src/ and fails if anything other than the helper calls ImGui's resize-id functions. Not reproduced live: the drop itself was not driven with a mouse, so that this is the report's path is a reading of ImGui's source and of the disassembly, not an observation. The application enables ImGui's docking but never creates a dock space over its main window (there is no DockSpace call in src/), so a page can be docked onto another page and not into the edge of the main window; that, read from the source and not tried with a mouse, is what this guards, and it is not an answer to the reporter's complaint that inserting a window at the bottom does not work.

A Linux scan helper whose pipe has no reader is no longer filed as a crash, and no longer takes its driver out of the session's scans. Reports 862234B08B9FC33D and 30BCD594C128766D (0.99.73) were six "crashes" from one Linux session in one second, every one exit 0x8D = 141 = 128 + SIGPIPE, and the last blamed the rtlsdr driver alone, which put a working RTL-SDR on the do-not-ask list for the rest of the session. The helper now ignores SIGPIPE, writes through a function that learns of a closed pipe from the write itself and ends with its own exit code, 74, after finishing the walk; the parent reads 74, and 141 from a helper that did not ignore it, as a closed pipe: the scan is retried and swept and counted as before, but nothing is written to the crash directory and no driver is remembered. A real abort or segfault while the parent is listening is reported exactly as before, and a helper the parent timed out is still a timeout. Verified on Windows by 545 checks in test_soapy_enum_proc: the same scan run as a closed pipe and as a real death, whole-bus, retry and sweep, with 0 reports and nothing remembered against 1 report and 3 reports with the driver remembered; the old reading, put back, failed 12 of them. What a Windows run cannot show: the helper's own SIGPIPE handling and the 141 path are POSIX-only and were type-checked against stubs with GCC, not run, and the Linux run in CI is the check. What is also not established: the parent holds the helper's stdout open until it has reaped it, so a closed stdout was not the field's pipe while the parent was waiting; the helper's inherited stderr, or a socket in a vendor module, is the suspect, and httplib::Server's constructor ignoring SIGPIPE for the whole process would make a helper started before the web server died where one started after did not. docs/DIAGNOSTICS.md lists the changed and new log lines.

The eight new interface texts (Range: Automatic, Range: Manual, Floor, Ceiling, Reset display to defaults, MANUAL, Right-click to change the range and display, and the three-line tip on the two Range keys) are in all 33 catalogues (py -3.14 tools/i18n_keys.py: 1976 keys, 1976 translated, 0 empty, 0 missing, 0 stale in each; before, 1968 translated and 8 missing in each), src/core/lang_assets.hpp was regenerated once with tools/embed-lang.py, and test_i18n (196,187 checks) and test_i18n_glyphs (487) pass. They are machine translations that nobody has read. Floor and Ceiling are the lower and upper limit in the sense the catalogues already give FLOOR / CEILING on the strength key, the MANUAL tag on that key is kept to one short word (an abbreviation in Greek, as AUTO already is there) because the key is not drawn at all when it would take more than 46% of the picture's width (its width in each language was not measured), and a patch's part is the word the catalogues already use for it. The two work packages of 0.99.76 together build in Release without an error (the build was run twice, because they add four test files), and the whole suite on that tree (0.99.76, before the last two changes of this release), 368 tests one at a time, passed 368 of 368 in 2,322 s.

The Fitted modules list and the DECODE bank's "Turn on and off plugins" list are now grouped under the store's own headings (AIRCRAFT, MARINE, SATELLITES AND WEATHER, METERS AND PAGING, VOICE AND DATA, MAPS AND TOOLS, OTHER), a double-click on a Fitted modules row opens that plugin's own window, and a click anywhere on a store card opens the plugin's page. A US tester wrote, through the owner: "I really like how the store groups plugins by function (Aircraft, Marine, Satellites and Weather, etc). That really makes it easier to browse. Implement this sorting on the Plugins panel and the 'Turn on and off plugins' menu as well: if you install them all, the list is quite long, intermixed and kind-of alphabetical." The owner added: "when you open the plugins Fitted modules window, when I double click on the plugin it should show the plugin window", and "on the plugin store the whole [card] should be clickable not just the text of the plugin."

A module's heading is the catalogue's category for it, found the same way the Fitted modules window already found it for the module's drawing (the install record names the catalogue entry, the entry names the category), now by one function that both lists use so they cannot put a module under two headings. Inside a group the order is by name, as the store's grid is. A module the catalogue has not filed - one dropped into the folder by hand, or any module before a catalogue has been read or kept - is listed last, under OTHER. While no module has a category at all, as on a fresh install, there are no headings and the Fitted modules list is the name-sorted list it was; the DECODE bank keeps the order the plugin folder is read in. The chips and the search filter first and keep working, and whether there are headings is a fact of the whole list, so they do not come and go as someone types.

A double-click on a Fitted modules row, anywhere but on its keys, shows the plugin's own window: its panel if it has one, otherwise its picture window, its instrument or its map page. A dormant plugin wakes through the ordinary lifecycle as the window opens (plugin: <name> woke - window open, one decoder created, nothing pinned). Nothing is tuned: the rail's window rows also tune to the plugin's preset and this does not. A double-click on the name or the picture works too: the first click opens the module's page, as it always did, and the second press hands the user the window instead and brings the list back. A module with no window of its own (a text decoder, which is read in the shared Decoder output window) and a stopped one (with its instances gone no window of its could draw) get their module page, which says so and offers START. A single click is unchanged.

The whole store card is now one click target, submitted under the glyph, the name and the key so that those take the pointer where they are: a click on GET or UPDATE is the key's alone and never also opens the page. One thing is given up on purpose: a press that starts on a store card is a click on the card, so dragging the store window by a card's empty part no longer starts there (the gap between cards, the top bar and the rails still do; no test exercises it).

A row in a plugin's panel can now be a key: where a plugin lists things to choose between, such as the IBOC Radio plugin's programmes HD1 to HD8, you press the row to choose it instead of going into the plugin's settings to type the number, and a plugin may now have sixteen command keys where it had eight. A US tester, through the owner, asked for exactly this: "If I click on HD2 on the screen below, it should select that program, instead of having to go into the plugin settings and change the Programme to 1." The owner's answer was "can you implement this". The row lights under the pointer, the hand cursor shows, the cell texts stay as the plugin wrote them, and a plugin's green, amber and dimmed rows still read as they did. A press reaches the plugin the way a press of one of its keys in Fitted modules does, and only if the plugin has registered that key: a row that names a key the plugin never added is drawn as the text it always was, because a key that does nothing is a lie. A heading and a separator are never keys. A host older than 0.99.77 draws such a row as plain text and refuses the ninth key, which a plugin that reads the new state bit (below) never meets. The same rows beneath an instrument's face are keys too. The browser interface never carried a plugin's panel rows, so nothing changes there.

For plugin authors: what a plugin must do to use key rows, and the plugin that does. The plugin ABI is still 3 and the host API level is unchanged (no CascadeHostApi member changed), and docs/PLUGIN-API.md has a new section, "Panel rows the user can press (0.99.77)". plugin_abi.h gains CASCADE_ROW_FLAG_KEY (0x00000008u), the command id in bits 8 to 15 of a row's flags (CASCADE_ROW_KEY_ID_SHIFT, CASCADE_ROW_KEY_ID_MASK, with CASCADE_ROW_KEY_FLAGS(id) to pack it, ORed with GOOD, WARN or MUTED, and CASCADE_ROW_KEY_ID(flags) to read it - a panel row has no size field to grow, so the id rides in the flags, as a track's category does), the state bit CASCADE_STATE_ROW_KEYS (0x00010000u, set in every CascadeReceiverState the host fills, with no grant) and CASCADE_MAX_COMMANDS_PER_PLUGIN 16 (it was 8). To use it a plugin reads CASCADE_STATE_ROW_KEYS from get_state and does nothing new unless it is set; when it is set the plugin registers the keys with add_command (ids 1 to 255), marks each row that should be pressable with CASCADE_ROW_KEY_FLAGS(id) for an id it registered, and takes the press with poll_command exactly as it takes a press on its plate. A plugin may say "press to choose" only when it has seen the bit, and should register the keys it can live without last, because a host before 0.99.77 refuses the ninth add_command with CASCADE_API_LIMIT. Junk left in bits 8 to 15 of a row that does not carry the flag is not read as a key. Both builders' copy of the new sentence beside the larger limit named CASCADE_ERR_LIMIT, which is not a name in the header; the integration corrected that one word to CASCADE_API_LIMIT, so the header is the application's src/core/plugin_abi.h with sha256 045197df1e416ee8a02e1c641f2cd5844d355b22cdb5550a41e854d57956e75b, and the plugin repository's include/plugin_abi.h must hold the same bytes (it does, as of the integration; the IBOC plugin repository's copy still has the old word until it is refreshed from the same file).

IBOC Radio (nrsc5 library) 0.1.1 is the first plugin to use it: it registers HD1 to HD8 as command ids 11 to 18 only when the host sets the bit and only if the host takes all eight (so a 0.99.74 or 0.99.75 host, or the unpublished 0.99.76 build, with its limit of eight, keeps the three keys it had: HD next, HD previous and Re-acquire), its PROGRAMMES rows become those keys with the heading "PROGRAMMES: press one to choose", and its dashboard picture says CHOOSE IN THE PANEL (the picture itself cannot be clicked). Without the bit the panel is exactly what 0.1.0 drew.

Verified on Windows, on the grouping and the pressable row applied together, with the real application. The grouping: test_plugins_view (680 checks) pins the group order, the heading boundaries, the flat list and the double-click precedence; test_plugin_store_view (387) pins the order both lists share, equal to the store's own sequence of groups; and four tests run cascade.exe. test_plugin_inuse_app (286) fits three real modules with a kept catalogue: the Fitted modules window lists them AIRCRAFT, VOICE AND DATA, OTHER with each heading above its own row (y 245, 348, 450, where the name order would put the OTHER module in the middle); the DECODE bank does the same (y 600, 751, 902); a double-click on the picture decoder's row opens its picture window, wakes it once and pins nothing, on the name as well, and the list is back. test_decoder_settings_fold_app (35) opens the panel window of the settings fixture by double-click and finds no heading in a bank with no catalogue, test_unplaced_list_app (42) opens the track fixture's map page, and test_plugin_store_app (237) clicks the line under a card's name, the strip along its bottom and its padding (each opens the page), the gap between cards (does not), and GET (fits, does not open the page). The pressable row: test_plugin_api (525) pins the three new definitions, the packing of every id from 0 to 255, the limit as a literal 16 (sixteen keys taken, the seventeenth refused) and which rows are pressable; test_plugin_settings_ui (328) that the state bit is set for every plugin the host answers, including one built before the receiver's locator existed; test_plugin_ui (15,140) that a keyed row's flags reach the host whole; and the new test_row_keys_app (88 checks, twelve runs of the real cascade.exe, 33 s) uses a fixture plugin written the way the IBOC panel is: HD1 and HD2 are listed as keys and the row bound to an id the plugin never registered as text, a click on HD2 hands the plugin id 12 once, HD1 then HD2 hands 11 then 12, a click on the unregistered row or on an old-style row hands it nothing, the rows beneath an instrument's face are keys too, and sixteen commands are sixteen keys on the module's page, two to a line, eight lines. Every one of these went red with its behaviour taken out (the builders' break-it runs are in their commit messages). The whole suite on the tree with all five changes, 369 tests one at a time, passed 369 of 369 in 2,394 s. These two changes added or changed no interface text: py -3.14 tools/i18n_keys.py says 1976 keys, 1976 translated, 0 empty, 0 missing, 0 stale in each of the 33 catalogues, before and after them (the 1976 are the 1968 of 0.99.75 and the eight words of the range menu above), so src/core/lang_assets.hpp was not regenerated for them, and test_i18n (196,187 checks) and test_i18n_glyphs (487) pass.

The real IBOC Radio (nrsc5 library) 0.1.1 module (the file nrsc5-radio-dynamic-0.1.1-abi3-win-x64.dll, sha256 a5846e01adb47f87189c5bf6f7e008c9399e3683def973fad8fdcf4632964c20) was run in this build on 2026-10-13, alone in a scratch plugins folder with every state redirected, in bounded runs with a scripted pointer. The UI census lists its eight PROGRAMMES rows as panelrow:IBOC Radio (nrsc5 library):11:key to :18:key; a click on the HD2 row, after scrolling the panel's table to it, moved the plugin's program setting to "1" in the saved configuration (the same run without the click saved none), and in a screenshot of the panel the > mark and the "NOW PLAYING" heading went from HD1 to HD2; a second identical run gave the same. The plugin did not find a libnrsc5 in that scratch environment, so what it shows is: Sync "no sync" with "libnrsc5 not found" beside it, and every programme read "(not announced)" and dimmed - and all eight are keys all the same, because a key does not depend on a signal.

Still not done, and known: nobody has pressed a real IBOC row in a released host (the run above had no library and no broadcast, so nothing was heard, and it is not a person with a mouse); the IBOC dashboard picture is not clickable and no click on a picture was tried (its PROGRAMMES box says CHOOSE IN THE PANEL); a panel's heading is drawn in its first column only, so "PROGRAMMES: press one to choose" is cut to "PROGRAMMES: press" until the window is made wider; only Windows x64 was built and run, so the Linux and arm64 jobs are the first to compile test_row_keys_app and its fixture and the changes to the lists; the instrument window's double-click is covered by a precedence unit test and not by a run, no fixture module declaring an instrument; the group headings in the DECODE bank were not looked at by eye in every theme; the IBOC plugin repository's copy of plugin_abi.h still carries the misnamed constant in a comment until it is refreshed from this tree's file; and everything the 0.99.75 notes list as not fixed or not verified is still so, as is everything the paragraphs above list as not fixed or not verified.

An old copy of a plugin whose name changed no longer loads beside the new one, so the DECODE bank lists the plugin once, and Dear ImGui's red "Programmer error: 2 visible items with conflicting ID!" box is switched off in the shipped program. This is a sixth change of 2026-10-13, made after the five above and shipped in 0.99.77 with them. A US tester on 0.99.75 sent a screenshot of the "Turn on and off plugins" list with two TETRA sections, "TETRA" and "TETRA Monitor (data only, no voice)", each with the same three presets and its own "+ Save 931.4375 MHz as a preset" key, and that red box open over the waterfall outlining both "+ Save" keys. He wrote: "Looks like something isn't being called out correctly with the two Tetra plugins or if there is only one, it is listed twice causing the error. Does this with all entries under Tetra." It was listed twice: the plugin's declared name was "TETRA" in its 0.1.0 file and "TETRA Monitor (data only, no voice)" from 0.1.1, someone who put 0.1.0 in by hand and later took 0.1.2 from the store has both files, and the host chose one copy of a plugin by declared name alone, so both ran. Their file names share a run id, tetra-decoder (the name without its version), which is the key the DECODE bank scopes a section's widgets by, so the two sections had identical widget ids, which is what the box complained of; the stop set, the saved presets and the dormancy clock are keyed on the same id and took two running modules for one plugin. The host now treats two loaded files with one run id as one plugin: the higher version runs, the older file is turned off with the words it always used ("Ignored: another copy of ... is installed. Version 0.1.2 in ... is loaded; this file is 0.1.0 and is not running. It can be deleted with Remove."), now naming the name of the section that stays, it reads REFUSED in Fitted modules with a REMOVE key, and CLEAN UP OLD VERSIONS counts it when the newer file is a store install and the old one a hand copy. Two different plugins (a different name and a different run id) never meet, and a plugin site's module whose file name is an official plugin's is held off by it whatever its version, as its declared name already was ("Not loaded: its file name is the official plugin ..."). The box says itself that it is for programmers' builds; it is off in a shipped run now, and on in a Debug build and in a bounded run with the census or the input script up, where the census notes imgui:idconflict when ImGui reports a conflict.

Verified on Windows, with the real application and a probe module standing in for the TETRA decoder, which was not available here (the probe of tests/fixtures/rescan_probe_plugin.cpp built twice: "Rescan Probe b" 1.0.0 and "Rescan Probe a" 1.1.0, copied under the file names probe-dup-1.0.0-abi3-win-x64.dll and probe-dup-1.1.0-abi3-win-x64.dll, one run id). test_plugin_host (435 checks) has a new case that failed 46 checks on the unchanged tree - the tester's pair stayed loaded, resolveDuplicatePlugins answered 0 where it must answer 1 - and passes: 0.1.2 runs, 0.1.0 is off with supersededBy naming the 0.1.2 file, in either order, equal versions go to the first record, three files over two renames leave one, a name shared by two run ids still resolves by name, three plugins that differ in both never meet, and a plugin site's module cannot displace an official one by its file name. test_plugin_cleanup (145 checks) failed 9 on the unchanged tree and the clean-up now finds the copy that stays by the file the host named. The new test_plugin_dup_app (37 checks, five runs of the real cascade.exe, 9 s) lists ONE section in the DECODE bank (decoders:entry:Rescan Probe a, y 606 to 629); with the grouping taken out the bank lists two (the old file's at y 600 to 623, the new file's moved down from 606 to 722), the old file is not refused, CLEAN UP OLD VERSIONS is not offered, and a pointer swept down the bank makes ImGui report the conflict (imgui:idconflict), which is also the proof that the test can see the conflict at all. In Fitted modules the old file reads REFUSED with a REMOVE key and no STOP key and the new one reads IDLE; with the new file recorded in installed.json and the old one not, CLEAN UP OLD VERSIONS is offered; a bounded run with no hook up logs imgui: ID-conflict box off (shipped build). test_imgui_debug_policy (10 checks) walks the table of when the box is on. Ten deliberate breakages each turned a named check red (the grouping removed, the loser left loaded, the lower version kept, ties to the later record, the chain followed one hop, the site rule off, the message naming the loser, the clean-up matching by name, the box never on, the box always on). The 15 suites that touch the area - these three, test_plugins_view (680), test_plugin_inuse_app (286), test_decoder_settings_fold_app (35), test_plugin_host_origin (48), test_plugin_cleanup_app (30), test_plugin_update_cleanup_app (77), test_plugin_rescan_skip (197), test_plugin_rescan_log (15), test_plugin_settings_ui (328), test_i18n (196,187) and test_theme_colours (3) - pass, run one at a time; the whole suite was not run for this change. It adds no interface text: the sentences are the host's own and are not translated.

Not verified, and known: nobody has run the real TETRA 0.1.0 and 0.1.2 files, or looked at the real window with them, so that the probe's folder is the tester's folder is a reading of his screenshot and of the code; that the red box is gone from the shipped program is read from the log line and the setting, not seen in a screenshot of a shipped build; ImGui reports a conflict only for the item the pointer is over, so a test needs the pointer on one; two files that are BOTH recorded in installed.json are not counted by CLEAN UP OLD VERSIONS (a file the store manages goes only by REMOVE, as before); and only Windows x64 was built.

Plugins on Linux

41 of the 43 plugins in the catalogue now have a Linux x64 build and a Linux arm64 build, FT8 Decoder 1.1.1 among them; before 2026-10-13, 24 had an x64 build and none had an arm64 one. This is a change to the plugin catalogue (commit 6f332d5 of the public catalogue), not to the application: the plugin store carries these builds, and nothing in the application's own files changed for them. Eleven x64 modules were rebuilt against glibc 2.35: three of them (AIS, EAS, Workbench) had been built against glibc 2.43 and could load on no current release of Ubuntu or Debian, and eight against glibc 2.38, which Ubuntu 22.04 and Debian 12 lack. The arm64 builds need glibc 2.38 (an Ubuntu 24.04 class system) and were tested only by CI, never on a Raspberry Pi. FT8 Decoder 1.1.1, whose preset opens the Decoder output window and which says why it is silent, was published for Windows and Linux.

Don't miss a new foxsdr release

NewReleases is sending notifications on new releases.