Skip to content

feat(linux): PipeWire port, AppImage packaging, and Linux bug fixes - #541

Open
ealtun21 wants to merge 17 commits into
fxsound2:develop/linuxfrom
ealtun21:feature/linux-port
Open

feat(linux): PipeWire port, AppImage packaging, and Linux bug fixes#541
ealtun21 wants to merge 17 commits into
fxsound2:develop/linuxfrom
ealtun21:feature/linux-port

Conversation

@ealtun21

@ealtun21 ealtun21 commented Jun 3, 2026

Copy link
Copy Markdown

Adds a Linux port using PipeWire and StatusNotifierItem.

Closes #19, closes #319, closes #317, closes #528

Audio pipeline

  • DSP and audiopassthru ported for PipeWire (float processing, interleaved ↔ planar)
  • Hard-clip after DSP to prevent digital overs

Tray / packaging

  • SNI + dbusmenu tray (Wayland, KDE Plasma, Waybar)
  • AppImage packaging + CI workflow (ubuntu-20.04, glibc 2.31)
  • PKGBUILD builds from local checkout, JUCE shallow-cloned

Bug fixes (cross-platform)

Linux-specific fixes

  • on_process() null-checked position before deref (realtime crash)
  • LinuxHotkeys::bindings_ data race between XCB and JUCE threads; added mutex, fixed destructor ordering
  • EQ band frequency had no effect on Linux
  • importPresets and export path separators fixed for Linux
  • updater.exe launch guarded behind _WIN32

Testing

  • Arch Linux (PipeWire 1.x, KDE Plasma 6), verified on two devices
  • CI workflow added but not yet run — pending GitHub Actions queue
  • Windows build not tested; cross-platform fixes are GUI/JUCE-layer only

Note: AI assistance (Claude) was used during development — primarily in the bug fixes and parts of the porting work. All code has been manually reviewed and tested on real hardware.

ealtun21 added 17 commits June 4, 2026 01:02
Replace `long` with `int32_t` across all DSP and audiopassthru sources;
`long` is 64-bit on linux x86_64 but 32-bit on windows, breaking
fixed-layout DSP memory banks.
Wrap windows-only APIs (COM init, dbghelp, NvOptimus exports) in
`#ifdef _WIN32`; add linux crash handler via execinfo backtrace.
Add portability shims in AudioPassthru.h (IMMDevice, WCHAR) for
non-windows builds.
Fix null-init pointer and swprintf bounds in DfxDspPreset.cpp; force
headphone_on_ = IS_FALSE to prevent muffled output on speakers.
Add hard-clipping to PipeWire output and fix integer overflow
in math conversion utilities. Prevents wrap-around distortion
when signals exceed nominal ranges.
The PipeWire RT thread races eqUpdateFromRegistry between the memory
write and registry write inside setEqBandBoostCut.  Setting
update_from_registry_=true first let the RT thread observe
memory=+12dB, registry=0, treat it as a mismatch, and reset the
filter to flat -- then clear the flag, making the reset permanent.
Fix: complete the ALL write first, then set flag to false.
Also fix isPowerOn() returning inverted bypass logic (bypass=0 is ON,
not OFF), and preset load loop skipping band 0 (b=1 -> b=0).
Hardcoded '\' made copyFileTo target invalid on linux,
silently failing every import so the preset list never updated.
updater.exe launch was unguarded -- crashes on linux since the
binary does not exist. Wrapped in #ifdef _WIN32.
Export reveal path used hardcoded L"FxSound\Presets\Export\"
which is invalid on linux. Replaced with getChildFile() chain.
- Replace JUCE popup tray menu with com.canonical.dbusmenu so Wayland
  panels (Waybar, KDE) render the context menu natively. Implements full
  feature parity with Windows: open, power toggle, preset select, output
  select, settings, theme, always-on-top, donate, exit.
- Fix icon install: fxsound_large.png (256x256) → hicolor/256x256/apps,
  fxsound.png (32x32) → hicolor/32x32/apps; run gtk-update-icon-cache
  post-install.
- Add packaging/build-appimage.sh: downloads linuxdeploy + appimagetool,
  builds Release binary, bundles deps, produces FxSound-<ver>-x86_64.AppImage.
  Uses NO_STRIP=1 to work around linuxdeploy strip incompatibility with
  .relr.dyn sections from gcc 14+.
- Update PKGBUILD: add post_install for icon cache + desktop db update.
… row

getSelectedItemIndex() counts the separator between app and user presets,
shifting all user preset indices by one. selecting the first imported preset
loaded the second; selecting the second went out of bounds and did nothing.
use getSelectedId() - 1 instead, since items are added with id = model_index + 1.
…lback

position can be null; dereferencing position->clock.duration without a check
would crash the realtime audio thread.
registerKey/unregisterKey/unregisterAll modify bindings_ on the juce message
thread while loop() reads it on the xcb event thread — a data race. add
bindings_mutex_ and lock it in all accessors. loop() now snapshots the matched
cmd under the lock and fires the callback outside it to avoid potential
deadlock. also fix destructor ordering: join the thread before calling
unregisterAll() so the thread is guaranteed idle during cleanup.
…ound2#521)

reinit() created fresh sliders initialized to 0 and called undoPreset() to
reload the preset into the dsp, but never called update() to copy those dsp
values back into the slider widgets. the graph drew a flat 0 dB line until
something else triggered equalizer_.update() (e.g. switching views). add an
explicit update() call at the end of reinit() so the graph reflects real band
values immediately after a 5↔10 band mode switch.
hotkey handlers for cmd_next_preset, cmd_previous_preset, and
cmd_next_output were guarded by getPowerState(), so they silently
did nothing when fxsound was turned off. removes the power check
so users can cycle presets and outputs with keyboard shortcuts at
all times (issue fxsound2#524).
windows: appends preset name to the existing NOTIFYICONDATA tooltip
after the output device line.
linux: adds setTitle() to FxTraySNI which updates the SNI Title
property and emits NewTitle so panels (KDE, Waybar) refresh the
hover tooltip. FxSystemTrayView::setStatus() populates it with
power state and active preset name on linux (issue fxsound2#520).
@ealtun21
ealtun21 marked this pull request as draft June 3, 2026 22:14
@ealtun21
ealtun21 marked this pull request as ready for review June 3, 2026 22:14
@bvijay74

bvijay74 commented Jun 5, 2026

Copy link
Copy Markdown
Member

Hi @ealtun21,
For Linux implementation integration I have created a new branch develop/linux. This is to avoid directly merging the Linux code to main branch.
Can you please create the pull request to develop/linux branch.

@ealtun21
ealtun21 changed the base branch from main to develop/linux June 5, 2026 05:04
@ealtun21

ealtun21 commented Jun 5, 2026

Copy link
Copy Markdown
Author

Hi @bvijay74,

I've retargeted this PR to develop/linux. Let me know if you need anything else.

Best,

@bvijay74

bvijay74 commented Jun 5, 2026

Copy link
Copy Markdown
Member

Thanks @ealtun21. I will review and merge the PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment