Comprehensive refactor of the cava module backend and frontends. Style & naming - Rename Cava -> CavaRaw; snake_case methods -> lowerCamelCase - Replace NULL with nullptr; replace C-style casts with static_cast - Add explicit standard-library includes (<memory>, <string>, <chrono>) - Fix missing trailing underscores on member variables Architecture - Remove Gtk::GLArea multiple inheritance in CavaGLSL (composition) - Return std::unique_ptr from factory; add doAction() to GLSL variant - Encapsulate thread timing arithmetic in AdaptiveDelay struct Thread safety & correctness - Replace raw sigc::signal with SafeSignal for cross-thread marshalling - Fix data race between loadConfig() and out_thread_ (recursive_mutex) - Fix audio_raw shallow-copy use-after-free via deep-copy AudioRaw payload - Make loadConfig() exception-safe with CavaConfigGuard RAII - Fix blocking read_thread_ race on shutdown (condition_variable + timeout join) - Fix isSilent() data race (acquire pthread mutex) - Eliminate doUpdate() recursion (iteration instead) - Guard audio_raw_clean() against uninitialized state - Fix format-icons underflow and cava_config buffer overflow - Store config by value to prevent dangling references on reload - Cache frontend config and refresh on runtime changes - Fix signed-char icon lookup bug on x86 - Prevent Json::Value mutation bloat via const-ref lookups - Broaden exception catches in worker threads (std::exception) Resource management & GL robustness - Fix OpenGL resource leaks (persist VBO/IBO/VAO; explicit destructor cleanup) - Fix shader error handling crashes (valid infoLog allocation) - Cache uniform locations instead of querying per frame - Fix gradient color uninitialized stack memory (zero-init + clamped count) - Fix shader time uniform integer division bug (float arithmetic) - Add explicit VAO bind in onRender() - Handle runtime surface config changes independently of shader changes - Clamp negative gradient_count before GL upload Follow-up - Singleton API split (inst() + configure()) intentionally deferred to a dedicated PR because it changes the public constructor contract.
154 lines
3.9 KiB
C++
154 lines
3.9 KiB
C++
#pragma once
|
|
|
|
#include <atomic>
|
|
#include <chrono>
|
|
#include <condition_variable>
|
|
#include <memory>
|
|
#include <mutex>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include <json/json.h>
|
|
#include <sigc++/sigc++.h>
|
|
|
|
#include "util/SafeSignal.hpp"
|
|
#include "util/sleeper_thread.hpp"
|
|
|
|
namespace cava {
|
|
extern "C" {
|
|
// Need sdl_glsl output feature to be enabled on libcava
|
|
#ifndef SDL_GLSL
|
|
#define SDL_GLSL
|
|
#endif
|
|
|
|
#include <cava/common.h>
|
|
|
|
#ifdef SDL_GLSL
|
|
#undef SDL_GLSL
|
|
#endif
|
|
}
|
|
} // namespace cava
|
|
|
|
namespace waybar::modules::cava {
|
|
|
|
class CavaBackend final {
|
|
public:
|
|
static std::shared_ptr<CavaBackend> inst(const Json::Value& config);
|
|
|
|
virtual ~CavaBackend();
|
|
// Methods
|
|
int getAsciiRange() const;
|
|
void doPauseResume();
|
|
void update();
|
|
const ::cava::config_params& getPrm() const;
|
|
std::chrono::milliseconds getFrameTimeMilsec() const;
|
|
|
|
struct AudioRaw {
|
|
std::vector<float> bars_raw;
|
|
std::vector<float> previous_bars_raw;
|
|
int number_of_bars = 0;
|
|
|
|
AudioRaw() = default;
|
|
explicit AudioRaw(const ::cava::audio_raw& raw) {
|
|
number_of_bars = raw.number_of_bars;
|
|
if (raw.bars_raw != nullptr && number_of_bars > 0) {
|
|
bars_raw.assign(raw.bars_raw, raw.bars_raw + number_of_bars);
|
|
}
|
|
if (raw.previous_bars_raw != nullptr && number_of_bars > 0) {
|
|
previous_bars_raw.assign(raw.previous_bars_raw, raw.previous_bars_raw + number_of_bars);
|
|
}
|
|
}
|
|
};
|
|
|
|
// Signal accessor
|
|
using SignalUpdate = SafeSignal<const std::string&>;
|
|
SignalUpdate& signalUpdate();
|
|
using SignalAudioRawUpdate = SafeSignal<AudioRaw>;
|
|
SignalAudioRawUpdate& signalAudioRawUpdate();
|
|
using SignalSilence = SafeSignal<>;
|
|
SignalSilence& signalSilence();
|
|
using SignalConfigChanged = SafeSignal<>;
|
|
SignalConfigChanged& signalConfigChanged();
|
|
|
|
private:
|
|
CavaBackend(const Json::Value& config);
|
|
util::SleeperThread read_thread_;
|
|
util::SleeperThread out_thread_;
|
|
|
|
// Cava API to read audio source
|
|
::cava::ptr input_source_{nullptr};
|
|
|
|
struct ::cava::error_s error_{}; // cava errors
|
|
struct ::cava::config_params prm_{}; // cava parameters
|
|
struct ::cava::audio_raw audio_raw_{}; // cava handled raw audio data(is based on audio_data)
|
|
struct ::cava::audio_data audio_data_{}; // cava audio data
|
|
struct ::cava::cava_plan* plan_{nullptr}; //{new cava_plan{}};
|
|
|
|
std::chrono::seconds fetch_input_delay_{4};
|
|
|
|
struct AdaptiveDelay {
|
|
std::chrono::milliseconds delay;
|
|
std::chrono::seconds delta{0};
|
|
|
|
explicit AdaptiveDelay(std::chrono::milliseconds initial = std::chrono::seconds(1))
|
|
: delay(initial) {}
|
|
|
|
bool increase() {
|
|
if (delta == std::chrono::seconds{0}) {
|
|
delta += std::chrono::seconds{1};
|
|
delay += delta;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool decrease() {
|
|
if (delta > std::chrono::seconds{0}) {
|
|
delay -= delta;
|
|
delta -= std::chrono::seconds{1};
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
std::chrono::milliseconds current() const { return delay; }
|
|
|
|
void reset(std::chrono::milliseconds new_delay) {
|
|
delay = new_delay;
|
|
delta = std::chrono::seconds{0};
|
|
}
|
|
};
|
|
|
|
AdaptiveDelay adaptive_delay_;
|
|
|
|
Json::Value config_;
|
|
int re_paint_{0};
|
|
bool silence_{false};
|
|
bool silence_prev_{false};
|
|
int sleep_counter_{0};
|
|
std::string output_{};
|
|
// Methods
|
|
void invoke();
|
|
void execute();
|
|
bool isSilent();
|
|
void doUpdate(bool force = false);
|
|
void loadConfig();
|
|
void freeBackend();
|
|
|
|
// Signal
|
|
SignalUpdate m_signal_update_;
|
|
SignalAudioRawUpdate m_signal_audio_raw_;
|
|
SignalSilence m_signal_silence_;
|
|
SignalConfigChanged m_signal_config_changed_;
|
|
|
|
std::atomic<bool> shutdown_{false};
|
|
bool audio_raw_initialized_{false};
|
|
mutable std::recursive_mutex state_mutex_;
|
|
|
|
// Synchronization for joining read_thread_ during destruction
|
|
bool read_thread_exited_{false};
|
|
mutable std::mutex read_thread_exit_mutex_;
|
|
std::condition_variable read_thread_exit_cv_;
|
|
};
|
|
} // namespace waybar::modules::cava
|