Commit Graph
481 Commits
Author SHA1 Message Date
Uladzimir KarpenkaandClaude Opus 4.8 fd145b28f6 Phase 3 (batch 6): route FM squelch + CTCSS through controller commands
The user plans to expose FM over web/CAT later, so FM controls get the
same controller treatment now — future web/CAT FM commands plug straight
into these.

- Relocate CTCSS_COUNT/CTCSS_TONES from MainForm to FMRepeater (a shared
  FM unit MainForm already uses) so TRadioController can drive CTCSS on the
  DSP. CTCSS_NAMES stays in MainForm (UI dropdown labels). MODE_FM is
  already visible to the controller via WDSPEngine.
- SetFMSquelch/SetFMSquelchLevel apply FM squelch (mode-gated) to the DSP;
  SetFMCTCSS/SetFMCTCSSTone clamp and apply CTCSS. All fire OnStateChanged.
- OnControllerState restyles the SQ/CTCSS buttons, syncs the squelch slider
  + label, and the CTCSS tone dropdown. Handlers/SetFMCTCSSTone call the
  commands (manual CTCSS still clears the auto-active flag in the handler).

FM repeater stays for the heavy batch — it needs XvtrTranslate/
ActiveTXFreqHz moved into the controller. ApplyFMSquelch remains for
band/mode re-apply paths (RestoreBand, XVTR).

Builds clean (cocoa).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 15:15:46 +03:00
Uladzimir KarpenkaandClaude Opus 4.8 7d3570346f Phase 3 (batch 5): route FM step through controller command
FM step is the only FM control exposed to another frontend (web:
SyncWebFMStep in, FWebServer.FMStepIdx out), so it's the FM param with
real cross-frontend value.

- TRadioController.SetFMStep clamps and fires OnStateChanged(rfFMStepIdx).
- OnControllerState updates the step dropdown, pushes FMStepIdx to the web
  server, and sets the FSpecView FM grid step (when in FM mode).
- MainForm.SetFMStep is now a thin wrapper over the command, so existing
  callers (band restore, dropdown select, web sync, XVTR restore) are
  unchanged.

FM squelch/CTCSS/repeater are intentionally left as-is: they are UI-only
(no web/CAT/daemon exposure) and already operate on controller state
fields, so routing them through commands adds ceremony without value for
the swappable-UI/daemon goals; CTCSS would also need CTCSS_TONES relocated
to a shared unit and repeater needs the network helpers (XvtrTranslate/
ActiveTXFreqHz) that belong to the heavy-command batch.

Builds clean (cocoa).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 15:10:44 +03:00
Uladzimir KarpenkaandClaude Opus 4.8 ad21516d48 Phase 3 (batch 4): route RX step attenuator through controller
SetAtten clamps, applies to the network (SetStepAtten), and fires
OnStateChanged; SyncWebAttn calls it. No desktop widget exists for the
attenuator, so there's no OnControllerState case — but state and the web
snapshot now flow through the single command path like everything else.

Builds clean (cocoa).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 15:00:04 +03:00
Uladzimir KarpenkaandClaude Opus 4.8 b3e4360a5d Phase 3 (batch 3): route Volume, AGC-top, WfAGC/WfNF through controller
First sliders through the controller, exercising the FSyncingFromController
re-entrancy guard:
- SetVolume/SetAGCTop clamp, apply to the DSP engine, update band cache,
  and fire OnStateChanged. SetAGCTop reuses ApplyAGCToEngine (the unified
  AGC index->enum mapping), fixing the same bad TWDSPAGCMode(index) cast
  that TrkAGCChange/SyncWebAGCTop used when re-applying AGC on level change.
- OnControllerState updates the slider/label/spectrum (rfAGCTop also
  refreshes the FSpecView AGC line, now consistent for web-initiated
  changes too) and the FSpecView waterfall flags (rfWfAGC/rfWfNF).
- TrkVolumeChange/TrkAGCChange (guarded), SyncWebVolume/AGCTop/WfAGC/WfNF,
  and ApplyWfAGCNF all call the commands.

Builds clean (cocoa).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 14:58:03 +03:00
Uladzimir KarpenkaandClaude Opus 4.8 f76533f209 Phase 3 (batch 2): route AGC mode through controller command
TRadioController.SetAGCMode now owns the index->WDSP mapping, applies to
the DSP engine, and updates the band cache, then fires OnStateChanged.
OnControllerState restyles the AGC button group. BtnAGCModeClick,
OnVfoOverlayAGCChange, and SyncWebAGC all call the command.

Fixes a latent web bug: SyncWebAGC used TWDSPAGCMode(index) directly,
mapping the UI index (0=FAST..4=OFF) onto the wrong enum (0=OFF...), so
web AGC selections set the wrong DSP mode. The button/overlay path used
the correct AGCModes[] array; unifying on the command applies that to web
too. Buttons/overlay behavior unchanged.

Builds clean (cocoa).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 11:33:45 +03:00
Uladzimir KarpenkaandClaude Opus 4.8 ff42889fa1 Phase 3 (batch 1): route NR/NB/SNB/ANF through controller commands
Apply the slice pattern to the noise-filter controls:
- TRadioController.SetNR/SetNB/SetSNB/SetANF now clamp and apply to the DSP
  engine, then fire OnStateChanged.
- OnControllerState refreshes the matching button (UpdateNR/NB/SNB/ANF).
- Click handlers, SyncWebNR/NB/SNB/ANF, and OnVfoOverlayDSPChange all call
  the commands instead of poking fields/widgets and the DSP directly.

UI clicks, the web client, and the VFO overlay now share one path; the
web client already renders these fields in applyState, so UI->web syncs.
Builds clean (cocoa); behavior unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 11:20:40 +03:00
Uladzimir KarpenkaandClaude Opus 4.8 a85f196c97 Phase 3 (slice): make Mute sync UI->web in the web client
The outbound channel already existed: PushLoop broadcasts the full
BuildStateJson (including "mute") to web clients every 200ms. The gap was
purely client-side — applyState never rendered the mute icon, so a
UI-initiated mute change was not reflected in the browser (the icon was
only updated on the client's own click).

WebPageHtml.pas:
- Add updMuteIc() helper; render it from applyState (guarded by
  isPending("mute")), so periodic state updates the icon.
- Click handler calls updMuteIc + pend("mute"); add "mute" to PEND_MAP so
  the optimistic local state isn't clobbered by an in-flight stale push.

Mute is now bidirectional. Other fields already render in applyState, so
UI->web works for them; this confirms the web outbound path is the
periodic full-state broadcast, not a per-field event push (the latter
will matter for the future serial/Arduino frontend).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 11:16:26 +03:00
Uladzimir KarpenkaandClaude Opus 4.8 b577a0d67a Phase 3 (slice): event-driven UI plumbing + Mute through controller
Vertical slice to establish the Phase 3 pattern before replicating it
across all commands: logic moves into controller commands, the UI becomes
event-driven, and frontends call commands instead of poking widgets.

- TRadioController.SetMute now carries the real logic (state + FDSPEngine.
  SetMute), then fires OnStateChanged(rfMute).
- MainForm subscribes via OnControllerState (controller -> UI). It restyles
  BtnMute from controller state. FSyncingFromController guards against
  re-entrancy when a programmatic widget change would retrigger OnChange.
- BtnMuteClick -> FController.ToggleMute; SyncWebMute -> FController.SetMute.
  The button/web no longer own the mute logic or the restyle.

Proves the command/event/refresh loop end-to-end (UI click and web both
route through the controller). Remaining commands follow this pattern.
Builds clean (cocoa); behavior unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 11:01:39 +03:00
Uladzimir KarpenkaandClaude Opus 4.8 2cf01036a3 Phase 2: move widget-stored state into TRadioController
NR/NB modes, SNB/ANF flags, and drive % were stored only in the widgets
themselves (Btn*.Tag, TrkDrive.Position) — the last hard blocker to a
headless/daemon mode, since without those controls the state had nowhere
to live and CAT/Web read them directly.

- NR/NB: BtnNR.Tag/BtnNB.Tag -> FController.FNRMode/FNBMode (Integer).
- SNB/ANF: BtnSNB.Tag/BtnANF.Tag (0/1) -> FController.FSNB/FANF (Boolean).
- Drive %: TrkDrive.Position -> new FController.FDrivePercent; the slider
  is now pure input (TrkDriveChange pushes its value into the controller),
  while FDriveLevel stays the derived hardware byte.

Buttons/sliders become pure view+input: click handlers and UpdateXButton
read/write the controller field; CAT getters, the web snapshot, and
GetSnapshot all source state from the controller, not the widgets.

No behavioral change. Builds clean (cocoa).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 10:48:51 +03:00
Uladzimir KarpenkaandClaude Opus 4.8 63eb2c701f Phase 1: extract TRadioController (state + engine ownership)
Decouple MainForm from CAT/Web by introducing a headless core,
TRadioController, as the single home for radio state and engine objects.
This is groundwork for swappable UIs, a daemon (Web-only) mode, and a
future hardware control panel — each becomes a frontend/adapter over the
same controller API.

RadioController.pas (new):
- Radio state + engine objects (Network/DSP/Audio/Settings/Channels) as
  public fields named exactly as in MainForm, so wiring is mechanical.
- Command API: absolute Set* plus relative *By/Up/Down (for encoders).
- OnStateChanged(field) notifications and GetSnapshot for frontend sync.
- Invoke/OnInvoke (TUIInvokeEvent) thread-marshalling seam (GUI uses
  TThread.Synchronize; daemon supplies its own).
- Commands are scaffolding for now (update field + fire event); engine
  logic stays in MainForm and migrates in later phases (TODO-wiring).

MainForm.pas:
- Owns FController; creates it first in FormCreate, frees it last in
  FormDestroy. Engines are still created/freed by MainForm into the
  controller's fields (ownership move is a later phase).
- ~1400 FXxx references redirected to FController.FXxx (implementation
  only); dead field declarations removed from the interface.

No behavioral change — pure state relocation. Builds clean (cocoa).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 10:43:01 +03:00
ew8bak 261b3190b8 BUILD.MD and inc numBuild 2026-06-02 18:08:10 +03:00
ew8bak 1e9796bc9a Enable build number auto-increment 2026-06-02 18:01:46 +03:00
ew8bak 2b55771176 Merge remote-tracking branch 'origin/feature/radio-speaker-toggle' 2026-06-02 17:47:35 +03:00
ew8bakandClaude Sonnet 4.6 f7e6e431c9 add ignoreversion flag to all files in inno setup
Without it Inno Setup skips files whose version matches the installed
one, so the exe was not replaced on updates.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 17:33:36 +03:00
ew8bakandClaude Sonnet 4.6 e9f4bf1732 fix Windows config dir: use %APPDATA% env var directly
FPC's GetAppConfigDir on Windows falls back to the exe directory
if the shell API call fails, which breaks installs in Program Files.
Read %APPDATA% from the environment variable instead.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 17:03:14 +03:00
ew8bak 474a638abc add portaudio dll inno setup 2026-06-02 15:58:35 +03:00
ew8bakandClaude Sonnet 4.6 710abce288 use %APPDATA%\ewsdr on Windows instead of exe directory
Program Files is read-only for normal users; GetAppConfigDir(False)
returns the proper per-user config location on all platforms.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 15:52:11 +03:00
ew8bak 3e1988c00e add opengl parameter innosetup 2026-06-02 15:05:13 +03:00
ew8bak 88a0dd9b80 fix version ewsdr.lpi 2026-06-02 14:34:23 +03:00
ew8bak 827c6b3207 add libfftw in ewsdr_inno_setup 2026-06-02 14:29:23 +03:00
ew8bak 1bb831d7b1 add other folder 2026-06-02 14:26:42 +03:00
Uladzimir KarpenkaandClaude Opus 4.8 aff17aa3a6 Add Audio tab toggle for transceiver built-in speaker
Adds a "Send audio stream to radio (built-in speaker)" checkbox on the
Settings → Audio tab. It is a single switch for the radio's built-in
speaker:

- ON  — RX audio is streamed to the radio via the DDC Audio packet
        (port 1028, 64 L/R pairs per packet) and the speaker is unmuted
        (High Priority byte 1400 bit1 = 0).
- OFF — speaker is muted (byte 1400 bit1 = 1, ANAN-8000DLE IO1 output)
        and no audio stream is sent.

The float->int16 conversion and 64-pair packet accumulation live in
THPSDRNetwork (SendSpeakerAudio/SetSpeakerAudio), next to SendDDCAudio;
MainForm only reads the setting and forwards the audio. State is
persisted per-MAC (send_audio_to_radio, default off) and re-applied on
each radio start.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 08:55:36 +03:00
ew8bak d8584323ac Revert "Add WFM (broadcast FM) demodulator support"
This reverts commit e52e32dc20.
2026-06-01 14:40:39 +03:00
ew8bakandClaude Opus 4.7 e52e32dc20 Add WFM (broadcast FM) demodulator support
Wires the new wdsp WFM API into the engine and adds a WFM preset to the
FM filter row (200 kHz BW / 75 kHz deviation). Also fixes WDSP_SAM
constant which was 12 — that slot is now RXA_WFM in the updated enum.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-31 22:40:24 +03:00
ew8bakandClaude Sonnet 4.6 83ec28f997 Prevent OS sleep and display blanking while SDR is running
Add PowerInhibit.pas with InhibitSleep/UninhibitSleep:
- Windows: SetThreadExecutionState (kernel32, no child process)
- macOS: caffeinate -d -i -s -w <PID> (auto-exits on crash via -w flag)
- Linux: systemd-inhibit with kill-0 poll loop (releases within 5s on crash)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 21:33:30 +03:00
Uladzimir KarpenkaandClaude Opus 4.8 655d242d02 Add Spectrum gradient fill toggle in settings
Mirror the existing Wideband gradient fill option for the main spectrum:
a "Spectrum gradient fill" checkbox on the Display settings tab gates the
under-curve gradient in TSpectrumView.DrawSpectrum. The curve, grid,
filter band and VFO markers always draw; only the gradient is optional.

Persisted as spectrum_fill in settings JSON (default True, preserving
current appearance). Wired through Settings -> SettingsForm visibility
event -> MainForm -> TSpectrumView.FillSpectrum, same path as
WidebandFill.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-29 15:24:33 +03:00
Uladzimir KarpenkaandClaude Opus 4.8 2a7bf8528a Cache SampleRateOverlay to a bitmap instead of redrawing per frame
The span-selector panel was rendered directly onto the spectrum canvas
every frame via DrawSelf, doing ~30 TextWidth + ~11 TextOut Canvas calls.
On Cocoa these text ops are very expensive (~10.6 ms/frame, ~30% of a
core) and dominated DrawSpectrum.

Render the panel once into a per-pixel-alpha cache bitmap and only
composite it over the spectrum each frame; rebuild only when state
changes (rate, hover, hidden, bounds). The cache is built with the
two-background method (render over black and white, derive straight
alpha + color) so layered semi-transparent button fills and antialiased
text composite over the live spectrum exactly as before.

Cuts the overlay phase from ~10.6 ms to ~0.2 ms per frame; DrawSpectrum
drops ~13.8 ms -> ~2.7 ms. Mirrors the caching VfoOverlay already uses.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-29 15:10:30 +03:00
Uladzimir KarpenkaandClaude Opus 4.8 c3a8a14110 Drop per-frame alpha-restore pass on macOS spectrum
On Cocoa the full-bitmap alpha-restore pass is unnecessary: Canvas
operations keep alpha at $FF and the cached grid background is opaque,
so removing it cuts a full-frame O(W*H) pass per spectrum frame with no
visual change (verified on macOS).

Kept under {$IFNDEF DARWIN} for Qt6/win32 (different drawing backends
may zero alpha on antialiased edges) until visually verified there.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-29 14:31:18 +03:00
Uladzimir KarpenkaandClaude Opus 4.8 55d5bf01cf Optimize CPU waterfall/spectrum rendering
WaterfallView: drop per-frame TLazIntfImage + LoadFromIntfImage (device
bitmap recreation + full pixel-format conversion every frame). Write
pixels directly into the bitmap via BeginUpdate/ScanLine like
SpectrumView already does, with platform-native byte order (ARGB on
Cocoa, BGRA elsewhere).

Replace per-pixel float colorization with a 512-entry dB->color LUT
rebuilt only when WfLow/WfHigh/theme change beyond a small threshold.

Turn the bitmap itself into a ring buffer: each frame builds a single
new row and writes one ScanLine; scrolling is done at paint time via two
CopyRect calls. Removes both full-frame passes per frame (image memmove
+ buffer->bitmap copy), cutting per-frame cost from O(W*H) to O(W). TX
mode preserved by leaving out-of-span columns untouched in the row buf.

SpectrumView: rewrite the per-frame alpha-restore pass from a strided
per-byte loop to a sequential per-dword OR with the alpha mask
(bit-identical output, sequential memory access).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-29 14:21:40 +03:00
Uladzimir Karpenka 16259ed704 add script create macOS bundle app 2026-05-29 08:00:22 +03:00
Uladzimir Karpenka 2ab02d7681 Store WDSP wisdom file in app config dir alongside settings JSON 2026-05-28 22:16:57 +03:00
Uladzimir Karpenka a02d35fcce Fix macOS pixel format: use ARGB byte order in all raw ScanLine blending
On macOS Cocoa, TBitmap.pf32bit stores pixels in ARGB order
   (byte0=A, byte1=R, byte2=G, byte3=B) rather than BGRA used on
   Linux/Windows. All raw ScanLine pixel blending and channel mapping
   is now guarded with { DARWIN} to use the correct offsets.

   Also fix PortAudio library discovery on macOS: probe .dylib names
   (including Homebrew paths) before falling through to .so/.dll.
2026-05-28 21:47:54 +03:00
Uladzimir Karpenka 4f127a30ad fix macOS build 2026-05-28 15:59:49 +03:00
ew8bakandClaude Sonnet 4.6 4f281ce400 Channel memory: apply and restore TX power, fix auto-clear after band switch
- ApplyChannel now sets TrkDrive from Ch.Power; saves pre-channel level in
  FPreChannelPower (works around TrkDriveChange clearing the field on assignment)
- Power is restored when tuning away from channel (CheckChannelActive) or on
  first VFO movement after returning to band with no active channel (ApplyVfoA)
- Manual drive slider move clears FPreChannelPower so restore is skipped

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-27 15:52:54 +03:00
ew8bakandClaude Sonnet 4.6 1175e42516 Add channel memory: CH button, dropdown, editor form, smart CTCSS/RPT auto-clear
- New ChannelStore.pas: TChannel record + JSON persistence (channels.json)
- New ChannelsForm.pas: full channel editor (list + fields, pre-fills from current radio state)
- CH button in RX block: left-click opens dropdown, right-click opens editor
- Active channel: button highlights with channel name, deactivates on VFO move
- ApplyChannel: auto-switches XVTR/HF band based on channel frequency; saves old
  band state before switching to prevent cache corruption
- SaveCurrentBand: skip save when VFO > 61 MHz (prevents HF cache corruption
  from VHF channel application without XVTR)
- DeactivateXvtr: save full XVTR state (was only saving LastFreq)
- CTCSS/RPT from channel marked as auto: cleared automatically when tuning away
  from channel frequency; survives band switches via FXvtrSettings auto-flags
- TXvtrEntry: add LastCTCSSAutoActive, LastFMRptAutoActive fields

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-27 15:39:37 +03:00
ew8bak d08317e835 fix UpdateRptAutoState 2026-05-27 13:47:52 +03:00
ew8bakandClaude Sonnet 4.6 e6f02db399 Reorganize left panel: add RX block, move AGC, fix DUP placement and panel restore
- Add PanelRXBlock between TX and Band with CTUN, VOL, NR/NB/SNB/ANF/MUTE
- Move AGC panel to sit directly below RX block for logical RX grouping
- Move DUP button into TX panel below MOX/TUN
- Rename PanelRX → PanelAGC to match its content
- Standardize block label top (2px) and first-control top (16px) across all
  panels: AGC, SQL, CTCSS, STEP were inconsistent
- Fix left panel width restore after hide: save actual DPI-scaled width before
  zeroing instead of hardcoding 232, preventing panel truncation on Win32 with
  display scaling enabled

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-27 12:37:47 +03:00
ew8bak 047c9b0997 Make VFO overlay more transparent 2026-05-27 09:53:41 +03:00
ew8bakandClaude Sonnet 4.6 dd9e0da29e Fix FM step snap: round to nearest instead of truncating down
(ClickFreq div StepHz)*StepHz always snapped downward; replaced with
((ClickFreq + StepHz/2) div StepHz)*StepHz so clicks above the midpoint
between two steps correctly snap to the higher one.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-26 19:50:55 +03:00
ew8bakandClaude Sonnet 4.6 3b62a502e2 Fix audio device not restored in settings: suppress callbacks during RefreshAudioDevices
RefreshAudioDevices was firing OnRXDevChange/OnTXDevChange when restoring
the combo selection, which called ApplyAudioDevice(-1,'') and wiped
FAudioOutDevName/FAudioInDevName before LoadValues could use them.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-26 19:47:35 +03:00
ew8bak 4a84cce6e2 Merge branch 'refactor/extract-modules'
Extract helpers out of MainForm into structured modules:
- BoardUtils: BoardTypeName, FreqToBandIdx (removes duplication with DeviceForm)
- WisdomBuilder: TWisdomBuildThread + TWisdomProgressDialog
- UISync: TDeviceFoundSync/TStatusUISync/TDDCSeqSync via callbacks
- PlatformUtils: HasOpenGLSpectrumSwitch, CurrentScreenDPI

Minor cleanup: unused local constants, stale comments in AudioOutput.
2026-05-26 19:38:50 +03:00
ew8bakandClaude Sonnet 4.6 f09c36074f Remove unused constants and clean up stale comments
- Remove TOP_VFO_BTN_H and VFO_BTN_W (unused local consts, compiler warned)
- Consolidate duplicate comment block in AudioOutput.WriteDouble
- Clean up AudioOutput callback comment header

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-26 19:38:20 +03:00
ew8bakandClaude Sonnet 4.6 1e7121f200 Move FreqToBandIdx to BoardUtils
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-26 19:26:32 +03:00
ew8bakandClaude Sonnet 4.6 9716b88b0c Extract helpers into BoardUtils, WisdomBuilder, UISync, PlatformUtils
- BoardUtils: BoardTypeName (removes duplication with DeviceForm)
- WisdomBuilder: TWisdomBuildThread + TWisdomProgressDialog
- UISync: TDeviceFoundSync/TStatusUISync/TDDCSeqSync via callbacks
  (avoids circular dependency, removes TObject casts)
- PlatformUtils: HasOpenGLSpectrumSwitch + CurrentScreenDPI

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-26 19:15:51 +03:00
ew8bak aa252cb741 Move wideband OpenGL background to primitives 2026-05-26 18:26:13 +03:00
ew8bak daa8043a23 Render wideband spectrum with OpenGL primitives 2026-05-26 18:01:58 +03:00
ew8bak ae1a7d9a6d Add wideband spectrum fill option 2026-05-26 17:52:02 +03:00
ew8bak f61290ad3d Use WDSP analyzer for wideband 2026-05-26 17:25:07 +03:00
ew8bak e0d49c7bfa Add wideband display pane 2026-05-26 16:24:07 +03:00
ew8bak 762fad420e Guard OpenGL mode on native Wayland 2026-05-26 11:06:55 +03:00