From 61c9951dae64e79cbc1983098b5fd52317578103 Mon Sep 17 00:00:00 2001 From: Benjamin Barenblat Date: Thu, 27 Jan 2022 11:10:37 -0500 Subject: pavmon, a simple tool to monitor the volume of a PulseAudio sink --- pulse.h | 242 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 242 insertions(+) create mode 100644 pulse.h (limited to 'pulse.h') diff --git a/pulse.h b/pulse.h new file mode 100644 index 0000000..e71e5f2 --- /dev/null +++ b/pulse.h @@ -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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +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 + // . + 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); + + 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 . Calls the specified callback each time the + // state of the connection changes. + void Connect( + pa_context_flags_t, + std::function 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 on_failure, + std::function on_data) const noexcept; + + // Subscribes to some subset of server event notifications (see + // 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 on_failure, + std::function 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> state_callback_; + mutable std::unique_ptr> + subscribe_callback_; +}; + +} // namespace pulse + +#endif // PAVMON_PULSE_H_ -- cgit v1.2.3