Process audio slider events asynchronously

This commit avoids piling up events if events are queued quicker, than
gBar can handle, causing severe slowdowns.
Instead, the requests are handled in a worker thread and excess events
are discarded.

Fixes: https://github.com/scorpion-26/gBar/issues/86
This commit is contained in:
scorpion-26 2024-04-03 16:11:36 +02:00
parent 0f943405d3
commit 6dd1ee6783
2 changed files with 85 additions and 4 deletions

View file

@ -288,14 +288,30 @@ namespace Bar
Widget* micSlider;
Button* audioIcon;
Button* micIcon;
static AsyncAtomicContext<double> 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<double> 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)

View file

@ -1,11 +1,15 @@
#pragma once
#include <atomic>
#include <iostream>
#include <optional>
#include <thread>
#include <unistd.h>
#include <string>
#include <unordered_map>
#include <vector>
#include <filesystem>
#include <map>
#include <mutex>
#include "Log.h"
@ -186,6 +190,53 @@ inline Process OpenProcess(const std::string& command)
}
}
template<typename Data>
struct AsyncAtomicContext
{
std::atomic<bool> running = false;
std::mutex queueLock;
std::optional<Data> 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<typename Data, typename Callback>
inline void ExecuteAsyncAtomically(AsyncAtomicContext<Data>& 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