diff options
Diffstat (limited to 'pulse.h')
-rw-r--r-- | pulse.h | 242 |
1 files changed, 242 insertions, 0 deletions
@@ -0,0 +1,242 @@ +// Copyright 2022 Benjamin Barenblat +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy of +// the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. + +// An idiomatic C++ interface to (parts of) PulseAudio. + +#ifndef PAVMON_PULSE_H_ +#define PAVMON_PULSE_H_ + +#include <pulse/context.h> +#include <pulse/def.h> +#include <pulse/introspect.h> +#include <pulse/mainloop-api.h> +#include <pulse/mainloop-signal.h> +#include <pulse/mainloop.h> +#include <pulse/proplist.h> + +#include <exception> +#include <functional> +#include <memory> +#include <string> +#include <string_view> +#include <utility> +#include <vector> + +namespace pulse { + +// The base exception type for all exceptions thrown by this library. +class Error final : public std::exception { + public: + static Error InFunction(std::string function_name) noexcept { + return Error(function_name + " failed"); + } + + explicit Error(std::string what) noexcept : what_(std::move(what)) {} + + Error(const Error&) = default; + Error& operator=(const Error&) = default; + Error(Error&&) noexcept = default; + Error& operator=(Error&&) noexcept = default; + + const char* what() const noexcept override { return what_.c_str(); } + + private: + std::string what_; +}; + +// A simple key-value store. +// +// This class is thread-compatible. +class PropertyList final { + public: + explicit PropertyList(); + + PropertyList(const PropertyList&); + + PropertyList& operator=(const PropertyList& other); + + PropertyList(PropertyList&& other) noexcept : list_(nullptr) { + *this = std::move(other); + } + + PropertyList& operator=(PropertyList&& other) noexcept { + swap(*this, other); + return *this; + } + + ~PropertyList() noexcept; + + // Adds or overwrites a mapping. Useful keys are #defined in + // <pulse/proplist.h>. + void Set(const char key[], std::string_view value); + + pa_proplist* get() noexcept { return list_; } + + friend void swap(PropertyList& left, PropertyList& right) noexcept { + using ::std::swap; + swap(left.list_, right.list_); + } + + private: + pa_proplist* list_; +}; + +// A main event loop implementation based on poll(2). +// +// This class is thread-compatible. +class PollMainLoop final { + public: + explicit PollMainLoop(); + + PollMainLoop(PollMainLoop&& other) noexcept + : loop_(nullptr), api_(nullptr), handling_signals_(false) { + *this = std::move(other); + } + + PollMainLoop& operator=(PollMainLoop&& other) noexcept { + swap(*this, other); + return *this; + } + + ~PollMainLoop() noexcept; + + // Configures process signals to be delivered to the event loop. + void HandleSignals(); + + // Undoes a HandleSignals call. + void StopHandlingSignals() noexcept { + pa_signal_done(); + handling_signals_ = false; + } + + // Runs the main loop. This function does not return until somebody invokes + // Quit or an error occurs; it returns the argument to Quit. + int Run(); + + // Instructs the main loop to stop and return the specified code. + void Quit(int retval) noexcept { api_->quit(api_, retval); } + + pa_mainloop* get() noexcept { return loop_; } + + friend void swap(PollMainLoop& left, PollMainLoop& right) noexcept { + using ::std::swap; + swap(left.loop_, right.loop_); + swap(left.api_, right.api_); + swap(left.handling_signals_, right.handling_signals_); + } + + private: + pa_mainloop* loop_; + pa_mainloop_api* api_; + + bool handling_signals_; +}; + +// An RAII object to control signal handling in the event loop. Call +// HandleSignals() on the event loop and then create one of these for each +// signal you'd like to handle. +// +// This class is thread-compatible. +class SignalEventSource final { + public: + // Starts handling a particular signal. Calls the specified callback whenever + // that signal is received. + explicit SignalEventSource(int signal, std::function<void()>); + + SignalEventSource(SignalEventSource&& other) noexcept : source_(nullptr) { + *this = std::move(other); + } + + SignalEventSource& operator=(SignalEventSource&& other) noexcept { + swap(*this, other); + return *this; + } + + ~SignalEventSource() noexcept; + + friend void swap(SignalEventSource& left, SignalEventSource& right) noexcept { + using ::std::swap; + swap(left.source_, right.source_); + } + + private: + pa_signal_event* source_; +}; + +// A connection to a PulseAudio server. +// +// This class is thread-compatible. +class Context final { + public: + explicit Context(const char application_name[], PropertyList&, PollMainLoop&); + + Context(Context&& other) noexcept : ctx_(nullptr) { + *this = std::move(other); + } + + Context& operator=(Context&& other) noexcept { + swap(*this, other); + return *this; + } + + ~Context() noexcept; + + // Connects to the default PulseAudio server. Set options by bitwise-ORing + // flags defined in <pulse/def.h>. Calls the specified callback each time the + // state of the connection changes. + void Connect( + pa_context_flags_t, + std::function<void(pa_context_state_t)> on_state_change) noexcept; + + // Disconnects from the server. + void Disconnect() noexcept { + pa_context_disconnect(ctx_); + state_callback_ = nullptr; + } + + // Requests information about a sink specified by name. Calls on_failure if + // the request fails; calls on_data if it succeeds. Each callback will only be + // called once. + void GetSinkInfo( + const char name[], std::function<void()> on_failure, + std::function<void(const pa_sink_info&)> on_data) const noexcept; + + // Subscribes to some subset of server event notifications (see <pulse/def.h> + // for allowable mask values and event types). Calls on_failure once if the + // request fails. If the request succeeds, calls on_data once every time an + // event occurs. + void Subscribe(pa_subscription_mask_t, std::function<void()> on_failure, + std::function<void(pa_subscription_event_type_t)> on_event) + const noexcept; + + friend void swap(Context& left, Context& right) noexcept { + using ::std::swap; + swap(left.ctx_, right.ctx_); + swap(left.state_callback_, right.state_callback_); + swap(left.subscribe_callback_, right.subscribe_callback_); + } + + private: + pa_context* ctx_; + + // These are pointers so the underlying std::function is always + // at the same address, even if this object gets moved. + std::unique_ptr<std::function<void(pa_context_state_t)>> state_callback_; + mutable std::unique_ptr<std::function<void(pa_subscription_event_type_t)>> + subscribe_callback_; +}; + +} // namespace pulse + +#endif // PAVMON_PULSE_H_ |