Skip to content

SDR++ iak v1.4.0-alpha

Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 19 Jul 17:14
· 158 commits to master since this release

A large UI-focused release. The headline is a touch-friendly interface overhaul that makes SDR++ iak usable with a finger on Android (and previewable on desktop): an Android-metrics style overlay, a Material 3 dark theme, single-finger drag-scroll with fling, pill-handle window splitters, and an IC-705-style direct frequency-entry keypad with an integrated band picker. A new shared PopupDialog widget brings consistent keyboard and back-gesture handling to every modal in the app. Alongside these, a batch of desktop input fixes, radio/DSP touches, and crash fixes ported from and reported against upstream SDR++.

The band stacking feature inspired by ICOM radios is work in progress, not working correctly yet.

The waterfall autoscale is work in progress: Both the algorithm and user interface will change.

Added

Touch-friendly UI

  • Touch-friendly UI style overlay (style::touchStyle): an Android-like sizing layer applied as the final step of every re-style path (startup, scale change, Android re-init, theme switch), in dp units — ~48 dp row pitch, 24 dp slider grabs, touch extra padding, thin rounded scrollbars, Material-like rounded borderless frames and popups. It touches sizes only, so it composes with every theme. On by default on Android, off elsewhere, and toggleable live via a "Touch-Friendly UI" checkbox in the Display menu for desktop preview. The overlay was later toned down to at most ~25% over desktop metrics so it stays compact on desktop.
  • Android Dark theme: a Material 3 dark palette for the touch UI — the baseline neutral surface ramp (#141218 → #36343B), a blue tone-80 accent (#A8C7FA), precomputed 8%/12% state-layer blends for hovered/pressed, opaque popups and transparent borders. Instrument colors (FFT hold, waterfall background, meter histogram) are kept functional.
  • Android: single-finger drag-scroll with fling for the menu and other scrollable windows. A recognizer sits ahead of the pinch recognizer in the input chain and withholds the touch-down from ImGui until the gesture is classified, so scrolling can never activate a widget under the finger: a vertical drag past 8 dp slop pans the window 1:1 (SetScrollY) and flings on release with hardware-timestamped velocity, a horizontal drag replays the press so sliders and the splitter keep working, a motionless ~200 ms hold replays the press (menu drag-reorder becomes press-hold-drag), and a quick tap replays press+release. Touches over the waterfall, top bar, scrollbars or vertical-slider column pass straight through with no added latency.
  • Android: touch-friendly window splitters with visible pill-shaped drag handles and haptic feedback (routed through a new backend::hapticTick() JNI call), replacing the invisible fat hit bands that stole touches from the menu scrollbar, the frequency scale and the top of the waterfall.
  • Radio: compact mode selector rendered as a wrapping toggle-button grid (a reusable doToggleButtonGrid widget, PowerSDR/Thetis-style button bank) instead of the 3-row Columns(4) radio buttons — rows auto-wrap and balance to the panel width, and display order is decoupled from DemodID (NFM WFM AM DSB RAW / LSB USB CW CW-R). The active mode is highlighted in the grid.

Frequency entry (IC-705-style F-INP) and band picker

  • IC-705-style F-INP direct frequency-entry keypad: holding any frequency digit for 0.5 s (mouse or touch, with a haptic tick on Android) opens a modal keypad — entry in MHz with a single decimal point, ENT zero-fills the digits below the last entered one, CE clears, and pressing . first re-enters the current MHz digits for quick in-band retunes. Hardware keyboard (digits, dot, Backspace, Enter, Escape) works alongside the buttons and committed values are clamped to the tunable range. The layout is a 4×4 grid with a Backspace key in the Android PIN-pad position and a double-height ENT. A dialpad button next to the frequency display opens the same keypad, shown only when the top bar has room without squeezing the volume slider.
  • F-INP band picker page: a second page in the F-INP modal (toggled BAND / F-INP) with a category-filtered 4-column band grid built from the selected band plan. Tapping a band tunes to its last-used frequency/mode (a new bandMemory config, containment-validated on restore), else the band plan's default frequency/mode, else mid-band with a heuristic mode convention; the channel step sets the VFO snap interval. Page and category selections persist.
  • Band-plan tuning defaults: scripts/enrich_bandplans.py merges default tuning info (default frequency, channel spacing, monitoring mode) from the KiwiSDR band database (dist.dx_config.json, jks-prv/KiwiSDR, GPLv3) into the band-plan JSON files, matching by service category and frequency overlap with ITU-region preference. Only values genuinely present in the source are written; a mode that merely repeats the band-type convention is filtered out. The script edits files surgically (preserving indentation and line endings byte for byte), is idempotent, and the loader ignores unknown keys so the files stay backward compatible.

Waterfall

  • Waterfall FFT min/max auto-range button ("A", under the Min/Max sliders): sets the low/high thresholds from the current FFT, scanning the middle 60% of the row to avoid band edges, filter roll-off and the DC/center spike, and persists the result. Ported from ericek111's fork, addressing AlexandreRouma#1729. Thanks to @ericek111 (Erik Brocko).
  • Waterfall colormaps from AlexandreRouma#1694. Thanks to @konung-yaropolk.

Other

  • SDRplay: per-device PPM frequency correction with persistence and live updates, adding InputFloat/InputDouble support to SmGui for the server-mode UI along the way. Addresses AlexandreRouma#1781. Thanks to @M0OPK.
  • Frequency manager: right-click on the FFT or waterfall creates a bookmark at the frequency under the cursor, seeded with the selected VFO's bandwidth and mode. Implemented entirely in the frequency_manager module via the existing input handler. Ports the idea from upstream PR AlexandreRouma#1476 (issue #1475). Thanks to @Zaryob.
  • Radio: CW pitch/offset preset value controls, with a step-and-popup value editor aligned to the other preset controls.
  • Theme selector moved into the Display panel (refs AlexandreRouma#1008).

Changed

  • PopupDialog widget: a new core widget that encapsulates the two idioms every modal-ish popup in the app used to hand-roll — a lifecycle that arms OpenPopup exactly once per request and detects an ImGui-level close (Android back gesture, click outside) so it can't be undone by an unconditional re-open next frame, and focus-scoped, edge-triggered Enter/keypad-Enter to apply and Escape to cancel. It was rolled out across the app:
    • GenericDialog rebuilt on top of it, fixing all seven call sites (frequency-manager delete confirms, source-offset delete, module-manager confirm/error, server-source busy/auth, iq_exporter error), which previously could not be dismissed by back gesture / click-outside and had no keyboard support.
    • Frequency manager dialogs (bookmark add/edit, new/rename list, select-lists) ported over, gaining consistent Enter=apply / Escape=cancel and back/click-outside dismissal, with paired open flags collapsed into mode enums so "both dialogs open at once" is unrepresentable. Saving a bookmark with Enter/keypad-Enter and dismissing with Escape fixes AlexandreRouma#1758.
    • Source-menu add-offset dialog and the scheduler's task-edit popup adopted the same lifecycle.
  • Android navigation reworked around the platform task model. One Back press now dismisses one UI layer (topmost popup/modal, then credits, then — earlier in development — the menu), unwinding nested popups per press; Back arrives through both dispatch models (KEYCODE_BACK on Android ≤ 15 and OnBackInvokedCallback on Android 16+ with targetSdk 36). In the final design Back no longer collapses the menu panel or offers to exit — the dismiss chain ends by backgrounding the app via moveTaskToBack, matching the Android 12+ default — and exiting is done by holding the hamburger menu button for 0.6 s (haptic tick, then an Exit confirmation modal that stops the SDR and finishes the task). The old "Back Button Asks to Exit" System option was removed with this change.
  • Desktop widgets gained subtle rounding (frames/grabs/tabs 3 dp, popups 5 dp, scrollbar grabs become pills); windows and children stay square since they run edge-to-edge. The touch overlay's rounding is separate.
  • Left-menu layout hardened against the touch style: width math in several panels (FolderSelect/FileSelect "…" row, frequency-manager list selector, sink volume slider, Spots enable column, module-manager buttons, WebSDR view) now computes from live FramePadding/ItemSpacing/GetFrameHeight() instead of hard-coded 1× pixel constants. Full-width action buttons (Record, Start/Stop, Apply, …) are inset via new ImGui::ActionButton()/BeginActionRow() helpers (16 dp touch / 10 dp desktop per side) so CollapsingHeader bars remain the only edge-to-edge elements. The menu-header enable checkbox alignment is likewise derived from style metrics.
  • The doFingerButton 3×-font-height touch-button wrapper was removed now that the touch style overlay owns touch sizing globally; call sites use plain ImGui::Button and measure with GetFrameHeight().
  • RDS PS Name, RadioText and PTY Name now decode the RDS/EBU G0 character repertoire to UTF-8 at read time (raw storage unchanged), and the repertoire glyphs Roboto-Medium provides are baked into the ImGui atlas so non-ASCII station text renders correctly. Based on the approach in AlexandreRouma#1164, reworked to translate at read time rather than switching storage to std::wstring. Thanks to @attah.
  • Debug menu (and the ImGui demo window) now compile only into development builds — any build with commits past the last vX.Y.Z release tag, or a Debug configuration, or a CI nightly (any GitHub Actions build that isn't a release-tag build). Release-tag artifacts ship without the menu; IMGUI_DISABLE_DEMO_WINDOWS reduces imgui_demo.cpp to stubs while keeping exported symbols.
  • Module-com contract headers (radio_interface.h, meteor_demodulator_interface.h, recorder_interface.h) moved into core/src. These interface-only enum headers were reached via cross-module include paths by seven modules — a layering violation once core itself became a consumer (the band picker) — and all 11 cross-module target_include_directories entries are now gone: modules depend only on core at compile time.
  • Android: the app now uses the IAK launcher icon; the inert proguardFiles line was removed from the release build (R8/ProGuard never ran, so the app ships unobfuscated, as expected for an open-source project).
  • CW filter defaults adjusted; the two showHzPresetInput int/float overloads were merged into a single if constexpr template; the Spots "Spot Lifetime" label now states its units (minutes).
  • Internal: SmGui DrawListElem scalar payloads folded into an anonymous union (one scalar is live per element), shrinking each element with no wire-format or field-access change.

Fixed

  • Startup crash on a machine with no audio output device: RtAudio enumeration left the device list empty, so the audio sink indexed past its end (ACCESS_VIOLATION before the GUI appeared). selectFirst()/selectById() are now bounds-guarded and the menu shows "No audio output devices". Guarding alone would have stalled the whole DSP pipeline (the undrained sink blocks the shared IQ front-end that also feeds the FFT), so when no real device can be opened the sink now falls back to a lazily-constructed dsp::sink::Null, keeping the spectrum/waterfall live headless. Reported as AlexandreRouma#1754.
  • Crash on window resize caused by an unsynchronized resize of the FFT buffers.
  • Waterfall FFT auto-range ("A") no longer persists a bogus range when clicked with no live source: before the first frame latestFFT held the −1000 "hide everything" sentinel (or uninitialized memory), which saturated or blanked the display and survived restart. The buffers are now initialized to the sentinel and getAutorangeValues() no-ops unless a real, finite frame exists.
  • Waterfall zoom (pinch on Android, Ctrl+scroll on desktop) is now anchored on the frequency under the first finger / mouse cursor instead of the view center, so the signal being tuned stays put on screen while zooming. The Android pinch recognizer keeps the cursor on the first finger (tracked by pointer id) rather than the pinch centroid.
  • Scroll wheel over the side menu or other panels no longer also tunes the VFO: the waterfall's hover-area flags were only recomputed while the waterfall was hovered, so a fast mouse exit left them stuck and the main window's wheel-to-tune (and arrow-key tuning) kept firing anywhere. The flags are now cleared whenever the waterfall isn't hovered, and the stuck digitHovered flag of the frequency display was fixed too.
  • Scroll-wheel tuning and the frequency display digits now work with precision touchpads and free-spinning wheels: fractional wheel deltas were truncated to zero and dropped; they are now accumulated and applied on whole steps.
  • The Zoom/Max/Min sliders next to the waterfall now block wheel events from scrolling the panel under them (the upstream SetItemUsingMouseWheel call targeted a text label, making it a no-op).
  • About (credits) dialog made usable on small touch screens: it previously closed on any touch, so the scrollbar shown when the dialog overflows the screen (e.g. phone landscape) could not be used. The content now scrolls by dragging anywhere in the dialog, the scrollbar is wider and can be grabbed without dismissing, and the dialog closes on a short tap or Escape.
  • Desktop menu-width splitter no longer goes dead under the touch style: it gated its hit test on root-window hover, but the hit band straddles child windows whose hover boxes the touch style expands by TouchExtraPadding, swallowing the responsive sliver. It now accepts hover from any child of the main window (guarded by IsAnyItemActive so drags starting on menu widgets aren't stolen) and keeps the resize cursor for the whole drag. Window splitters also no longer jump to the grab point (this removes a small cursor-offset jump with the mouse on desktop too).
  • The touch style overlay is now re-applied on a runtime theme switch (ThemeManager::applyTheme() resets rounding to zero, which previously dropped the overlay).
  • KiwiSDR map: the selected-server info labels can no longer grow a scrollbar and let the wheel/touch drag scroll the map out of place; overflow is clipped instead, panning stays with the map's own drag handling.
  • SDRplay: a no-op PPM device update (sdrplay_api_Update_Dev_Ppm) is now skipped when the value hasn't actually changed.
  • Android: the drag-scroll recognizer's per-frame tick() bails out early when idle instead of calling steady_clock::now() every render frame; the F-INP keypad respects the source tuning range; and an out-of-bounds digits[] write when several typed characters arrived in one frame near the last digit was fixed.
  • Minor: the baseband NR CPU-load widget ID stabilized; the source-offset input is hidden in the no-offset mode.

Full Changelog: v1.2.2-beta3...v1.4.0-alpha