Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
dbus.hpp
Go to the documentation of this file.
1#pragma once
2
3// Minimal session-bus D-Bus client over libdbus-1, loaded at runtime (no
4// build-time libdbus-dev dependency, mirroring the libudev approach). Linux is
5// the only real backend; on other platforms every call is an honest no-op
6// (library_available() / connect_session() return false). Lives in
7// core/platform — the lowest layer — so any subsystem can use it for
8// session-bus IPC (xdg-desktop-portal file chooser / screenshot / notifications,
9// etc.) without a build-time dependency or a layering cycle.
10//
11// Two consumption models live side by side here, deliberately kept separate:
12//
13// * Portal CLIENT (file_chooser): a method call is sent and the reply / a
14// matching signal is awaited by *popping* messages off the incoming queue
15// (read_write + pop_message). pop_message removes a message BEFORE the
16// libdbus object-path dispatcher would ever see it.
17//
18// * Object SERVER (register_object / emit_signal / dispatch): the app EXPORTS
19// objects and answers incoming method calls. This requires the libdbus
20// *dispatcher* to run so registered handlers fire — driven by dispatch(),
21// which calls dbus_connection_read_write_dispatch(). It must NOT be mixed
22// with the portal pop_message pump on the same pending traffic: pop_message
23// would steal the very method call a registered handler is meant to answer.
24//
25// The object-server layer is AT-SPI-agnostic generic infrastructure: it answers
26// any registered object path, and is independently useful for MPRIS, desktop
27// notifications, or AT-SPI accessibility export.
28
29#include <functional>
30#include <map>
31#include <memory>
32#include <optional>
33#include <string>
34#include <vector>
35
36namespace pulp::platform {
37
38class DBus {
39public:
42
45 static bool library_available();
46
51
59
60 bool connected() const;
61
81
84 bool a11y_connected() const;
85
89 std::string unique_name() const;
90
93 struct PortalResult {
94 int response = 2; // portal "response" code
95 std::vector<std::string> uris; // results["uris"] (file:// URIs)
96 };
97
104 std::optional<PortalResult> file_chooser(
105 const std::string& method,
106 const std::string& title,
107 const std::map<std::string, std::string>& options,
108 const std::map<std::string, bool>& bool_options,
109 int timeout_ms = 120000);
110
111 // ── Object-server layer (generic, AT-SPI-agnostic) ──────────────────────
112 //
113 // A thin facade over the libdbus marshalling iterator. Reader walks an
114 // incoming message's arguments; Writer appends arguments to a reply or a
115 // signal. Both are non-owning views over the underlying DBusMessageIter and
116 // are only valid for the duration of the call that produced them.
117
120 class Reader {
121 public:
124 Reader() = default;
125
128 bool read_string(std::string& out);
129 bool read_int32(int& out);
130 bool read_uint32(unsigned& out);
131 bool read_double(double& out);
134 bool read_byte(unsigned char& out);
136 int arg_type() const;
138 bool next();
139
146 bool recurse(Reader& out_sub);
147
148 private:
149 friend class DBus;
150 Reader(DBus* owner, void* iter) : owner_(owner), iter_(iter) {}
151 // A recurse() sub-iterator owns its DBusMessageIter buffer; the parent
152 // hands ownership to the sub-Reader so it outlives the call returning it.
153 DBus* owner_ = nullptr;
154 void* iter_ = nullptr; // DBusMessageIter* (non-owning view)
155 std::shared_ptr<void> owned_iter_; // set only for recurse() sub-readers
156 };
157
161 class Writer {
162 public:
163 bool append_string(const std::string& s);
164 bool append_object_path(const std::string& p);
165 bool append_bool(bool v);
166 bool append_int32(int v);
167 bool append_uint32(unsigned v);
168 bool append_double(double v);
171 bool append_byte(unsigned char v);
172
175 struct Container { void* iter = nullptr; bool open = false; };
176
180 Container open_array(const std::string& element_signature);
184 Container open_variant(const std::string& value_signature);
188
190 Writer sub(Container& c);
191
192 private:
193 friend class DBus;
194 Writer(DBus* owner, void* iter) : owner_(owner), iter_(iter) {}
195 DBus* owner_ = nullptr;
196 void* iter_ = nullptr; // DBusMessageIter*
197 };
198
204 class CallContext {
205 public:
206 const std::string& path() const { return path_; }
207 const std::string& interface() const { return interface_; }
208 const std::string& member() const { return member_; }
209 const std::string& sender() const { return sender_; }
210
212 Reader& args() { return reader_; }
213
220 void error(const std::string& name, const std::string& message);
221
222 private:
223 friend class DBus;
224 CallContext(DBus* owner, void* msg, void* args_iter)
225 : owner_(owner), msg_(msg), reader_(owner, args_iter) {}
226 DBus* owner_ = nullptr;
227 void* msg_ = nullptr; // incoming DBusMessage*
228 void* reply_msg_ = nullptr; // DBusMessage* built by reply()/error()
229 void* reply_iter_ = nullptr; // heap append-iterator for reply(), freed
230 // by the server after the message is sent
231 bool answered_ = false;
232 std::string path_, interface_, member_, sender_;
233 Reader reader_;
234 };
235
240 using IncomingHandler = std::function<bool(CallContext& ctx)>;
241
245 bool register_object(const std::string& path, IncomingHandler handler);
247 bool unregister_object(const std::string& path);
248
252 bool emit_signal(const std::string& path, const std::string& interface,
253 const std::string& member,
254 const std::function<void(Writer&)>& build_args);
255
261 bool dispatch(int timeout_ms = 0);
262
271 bool call_method(const std::string& destination, const std::string& path,
272 const std::string& interface, const std::string& member,
273 const std::function<void(Writer&)>& build_args,
274 const std::function<void(Reader&)>& read_reply,
275 int timeout_ms = 5000);
276
277 // ── Signal subscription (broadcast, BYPASSES the object-path vtable) ─────
278 //
279 // D-Bus signals are broadcast messages, not method calls: they never reach
280 // the object-path vtable / trampoline (which only routes method calls).
281 // Receiving them requires (a) an org.freedesktop.DBus match rule so the
282 // broker delivers them to this connection, and (b) a libdbus connection
283 // FILTER that inspects each incoming message. Both are installed here.
284 // Matching signals are routed to handlers during dispatch().
285
288 using SignalHandler = std::function<void(const std::string& path,
289 const std::string& interface,
290 const std::string& member,
291 Reader& args)>;
292
297 unsigned add_signal_handler(const std::string& interface,
298 const std::string& member,
299 SignalHandler handler);
304 bool remove_signal_handler(unsigned token);
305
311 bool get_managed_objects(const std::string& destination,
312 const std::string& path,
313 const std::function<void(const std::string& obj_path,
314 const std::string& interface)>& per_object,
315 int timeout_ms = 5000);
316
317 DBus(const DBus&) = delete;
318 DBus& operator=(const DBus&) = delete;
319
320private:
325 bool connect_bus(int bus_type);
326
327 struct Impl;
328 Impl* impl_ = nullptr;
329};
330
333std::string file_uri_to_path(const std::string& uri);
334
335} // namespace pulp::platform
Context for one incoming method call against a registered object.
Definition dbus.hpp:204
const std::string & member() const
Definition dbus.hpp:208
Writer reply()
Begin a success reply.
const std::string & interface() const
Definition dbus.hpp:207
const std::string & sender() const
Definition dbus.hpp:209
friend class DBus
Definition dbus.hpp:223
Reader & args()
Argument reader over the incoming call.
Definition dbus.hpp:212
const std::string & path() const
Definition dbus.hpp:206
void error(const std::string &name, const std::string &message)
Answer with a D-Bus error (e.g.
Reads basic + container values out of an incoming message's argument list.
Definition dbus.hpp:120
int arg_type() const
Current argument's D-Bus type code, or 0 ('\0') when exhausted.
bool next()
Advance to the next argument. Returns false when there is no next.
bool read_uint32(unsigned &out)
bool read_string(std::string &out)
Read a string ('s') or object-path ('o') at the cursor into out and advance.
friend class DBus
Definition dbus.hpp:149
bool read_double(double &out)
Reader()=default
Default-constructed inert Reader — every read returns false.
bool recurse(Reader &out_sub)
Descend into the container (struct / array / variant / dict-entry) at the cursor, returning a sub-Rea...
bool read_byte(unsigned char &out)
Read a byte ('y') at the cursor into out and advance.
Appends values to an outgoing message (reply or signal).
Definition dbus.hpp:161
bool append_uint32(unsigned v)
friend class DBus
Definition dbus.hpp:193
bool append_byte(unsigned char v)
Append a byte ('y').
Container open_dict_entry()
Open a dict-entry (only valid inside an a{..} array).
bool append_object_path(const std::string &p)
bool close_container(Container &c)
Container open_variant(const std::string &value_signature)
Open a variant holding a single value of value_signature (e.g. "s").
Container open_struct()
Open a struct ("(...)" — no signature string for libdbus structs).
Container open_array(const std::string &element_signature)
Open an array.
bool append_string(const std::string &s)
Writer sub(Container &c)
Append into an already-open container instead of the top level.
DBus & operator=(const DBus &)=delete
bool remove_signal_handler(unsigned token)
Remove a signal handler previously returned by add_signal_handler().
bool connected() const
bool unregister_object(const std::string &path)
Remove a previously registered object. Returns false if not registered.
bool register_object(const std::string &path, IncomingHandler handler)
Export an object at path; handler answers method calls routed to it.
std::function< void(const std::string &path, const std::string &interface, const std::string &member, Reader &args)> SignalHandler
Handler invoked for each matching signal.
Definition dbus.hpp:288
bool dispatch(int timeout_ms=0)
Run the libdbus dispatcher once so registered object handlers fire and outgoing traffic flushes.
bool connect_a11y_bus()
Switch this DBus over to the desktop accessibility ("a11y") bus.
std::function< bool(CallContext &ctx)> IncomingHandler
Handler for incoming method calls on a registered object path.
Definition dbus.hpp:240
bool a11y_connected() const
True iff this DBus is currently backed by the a11y bus connection (i.e.
std::string unique_name() const
This connection's unique bus name (e.g.
bool emit_signal(const std::string &path, const std::string &interface, const std::string &member, const std::function< void(Writer &)> &build_args)
Emit a signal from path/interface/member.
bool connect_system()
Connect to the SYSTEM bus.
bool get_managed_objects(const std::string &destination, const std::string &path, const std::function< void(const std::string &obj_path, const std::string &interface)> &per_object, int timeout_ms=5000)
Call org.freedesktop.DBus.ObjectManager.GetManagedObjects on destination/path and invoke per_object(o...
bool connect_session()
Connect to the session bus.
bool call_method(const std::string &destination, const std::string &path, const std::string &interface, const std::string &member, const std::function< void(Writer &)> &build_args, const std::function< void(Reader &)> &read_reply, int timeout_ms=5000)
Generic blocking method call (object-server companion to register_object).
static bool library_available()
True iff libdbus-1 can be loaded right now (Linux dlopen probe).
DBus(const DBus &)=delete
std::optional< PortalResult > file_chooser(const std::string &method, const std::string &title, const std::map< std::string, std::string > &options, const std::map< std::string, bool > &bool_options, int timeout_ms=120000)
Invoke org.freedesktop.portal.FileChooser.
unsigned add_signal_handler(const std::string &interface, const std::string &member, SignalHandler handler)
Subscribe to signals matching interface+member (member empty = any member of the interface).
Device capability vocabulary: one tier ladder, one thermal ladder, and the projection from observed i...
Definition child_process.hpp:15
std::string file_uri_to_path(const std::string &uri)
Convert a file:// URI to a local filesystem path (percent-decoded).
Reply value for an xdg-desktop-portal request: the response code (0 = success, 1 = user cancelled,...
Definition dbus.hpp:93
std::vector< std::string > uris
Definition dbus.hpp:95
int response
Definition dbus.hpp:94
A nested container being built.
Definition dbus.hpp:175
void * iter
Definition dbus.hpp:175
bool open
Definition dbus.hpp:175