diff --git a/src/Bar.cpp b/src/Bar.cpp index 3283d6c..8a5b331 100644 --- a/src/Bar.cpp +++ b/src/Bar.cpp @@ -288,14 +288,30 @@ namespace Bar Widget* micSlider; Button* audioIcon; Button* micIcon; + + static AsyncAtomicContext sinkAsyncContext; void OnChangeVolumeSink(Slider&, double value) { - System::SetVolumeSink(value); + // Process async and atomically, so the event handler isn't filled up + ExecuteAsyncAtomically( + sinkAsyncContext, + [](double volume) + { + System::SetVolumeSink(volume); + }, + value); } + static AsyncAtomicContext sourceAsyncContext; void OnChangeVolumeSource(Slider&, double value) { - System::SetVolumeSource(value); + ExecuteAsyncAtomically( + sourceAsyncContext, + [](double volume) + { + System::SetVolumeSource(volume); + }, + value); } // For text @@ -304,7 +320,14 @@ namespace Bar { audioVolume += delta; audioVolume = std::clamp(audioVolume, 0.0, 1.0); - System::SetVolumeSink(audioVolume); + // Process async and atomically, so the event handler isn't filled up + ExecuteAsyncAtomically( + sinkAsyncContext, + [](double volume) + { + System::SetVolumeSink(volume); + }, + audioVolume); } double micVolume = 0; @@ -312,7 +335,14 @@ namespace Bar { micVolume += delta; micVolume = std::clamp(micVolume, 0.0, 1.0); - System::SetVolumeSource(micVolume); + // Process async and atomically, so the event handler isn't filled up + ExecuteAsyncAtomically( + sourceAsyncContext, + [](double volume) + { + System::SetVolumeSource(volume); + }, + micVolume); } void OnToggleSink(Button& button) diff --git a/src/Common.h b/src/Common.h index 552ba48..f3526f5 100644 --- a/src/Common.h +++ b/src/Common.h @@ -1,11 +1,15 @@ #pragma once +#include #include +#include +#include #include #include #include #include #include #include +#include #include "Log.h" @@ -186,6 +190,53 @@ inline Process OpenProcess(const std::string& command) } } +template +struct AsyncAtomicContext +{ + std::atomic running = false; + std::mutex queueLock; + std::optional queue; +}; + +// Executes the callback function asynchronously, but only one at a time. +// Multiple requests at once are stored in a FIFO of size 1. +// The context should point to a static reference. +template +inline void ExecuteAsyncAtomically(AsyncAtomicContext& context, const Callback& callback, const Data& data) +{ + // Update the queue + context.queueLock.lock(); + context.queue = data; + context.queueLock.unlock(); + + if (!context.running) + { + // Launch the thread + context.running = true; + std::thread( + [&, callback]() + { + while (true) + { + context.queueLock.lock(); + if (!context.queue.has_value()) + { + context.queueLock.unlock(); + break; + } + Data data = std::move(*context.queue); + context.queue = {}; + context.queueLock.unlock(); + + // Execute + callback(std::move(data)); + } + context.running = false; + }) + .detach(); + } +} + // Plugins #include "Window.h" #define DL_VERSION 1