summaryrefslogtreecommitdiff
path: root/pulse.h
diff options
context:
space:
mode:
authorGravatar Benjamin Barenblat <bbarenblat@gmail.com>2022-01-27 11:10:37 -0500
committerGravatar Benjamin Barenblat <bbarenblat@gmail.com>2022-01-27 11:10:37 -0500
commit61c9951dae64e79cbc1983098b5fd52317578103 (patch)
treec8d4c2c4fb7eacd660dccbde09b6cc216e2ccee3 /pulse.h
pavmon, a simple tool to monitor the volume of a PulseAudio sinkHEADmain
Diffstat (limited to 'pulse.h')
-rw-r--r--pulse.h242
1 files changed, 242 insertions, 0 deletions
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 <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_