192 static void iter_append(DBusMessageIter* iter,
const AVector<T>& t) {
195 if (!dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, s, &sub)) {
196 throw AException(
"dbus_message_iter_open_container failed");
199 dbus_message_iter_close_container(iter, &sub);
202 for (
const auto& v : t) {
203 aui::dbus::iter_append(&sub, v);
206 static AVector<T> iter_get(DBusMessageIter* iter) {
207 if (
auto got = dbus_message_iter_get_arg_type(iter); got != DBUS_TYPE_ARRAY) {
208 throw AException(
"type error: array expected, got '{:c}'"_format(got));
211 dbus_message_iter_recurse(iter, &sub);
215 result << aui::dbus::iter_get<T>(&sub);
216 if (!dbus_message_iter_next(&sub))
break;
227 static void iter_append(DBusMessageIter* iter,
const AMap<K, V>& t) {
230 throw AException(
"dbus_message_iter_open_container failed");
233 dbus_message_iter_close_container(iter, &sub);
236 for (
const auto&[k, v] : t) {
237 DBusMessageIter item;
238 dbus_message_iter_open_container(&sub, DBUS_TYPE_DICT_ENTRY,
nullptr, &item);
240 dbus_message_iter_close_container(&sub, &item);
243 aui::dbus::iter_append(&item, k);
244 aui::dbus::iter_append(&item, v);
247 static AMap<K, V> iter_get(DBusMessageIter* iter) {
248 if (
auto got = dbus_message_iter_get_arg_type(iter); got != DBUS_TYPE_ARRAY) {
249 throw AException(
"type error: array expected, got '{:c}'"_format(got));
252 dbus_message_iter_recurse(iter, &sub);
256 AUI_ASSERT(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_DICT_ENTRY);
257 DBusMessageIter item;
258 dbus_message_iter_recurse(&sub, &item);
259 auto k = aui::dbus::iter_get<K>(&item);
260 if (!dbus_message_iter_next(&item)) {
263 auto v = aui::dbus::iter_get<V>(&item);
265 if (!dbus_message_iter_next(&sub))
break;
276 static void iter_append(DBusMessageIter* iter,
const std::tuple<Types...>& t) {
277 if (
auto got = dbus_message_iter_get_arg_type(iter); got != DBUS_TYPE_STRUCT) {
278 throw AException(
"type error: struct expected, got '{:c}'"_format(got));
282 dbus_message_iter_recurse(iter, &sub);
284 std::apply([&](
const auto&...
args){
285 (..., aui::dbus::iter_append(&sub,
args));
289 static std::tuple<Types...> iter_get(DBusMessageIter* iter) {
290 if (
auto got = dbus_message_iter_get_arg_type(iter); got != DBUS_TYPE_STRUCT) {
291 throw AException(
"type error: struct expected, got '{:c}'"_format(got));
295 dbus_message_iter_recurse(iter, &sub);
298 return aui::tuple_visitor<std::tuple<Types...>>::for_each_make_tuple([&]<
typename T>() {
302 auto v = aui::dbus::iter_get<T>(&sub);
303 hasNext = dbus_message_iter_next(&sub);
342 using AException::AException;
345 static ADBus& inst();
348 Return callBlocking(
const AString& bus,
352 const Args&... args) {
359 auto formatError = [&](
const AString& info) {
360 return "unable to invoke {};{};{};{}: {}"_format(bus, path, interface, method, info);
367 DBusMessageIter dbusArgs;
368 dbus_message_iter_init_append(msg.get(), &dbusArgs);
370 aui::parameter_pack::for_each([&](
const auto& v) {
371 aui::dbus::iter_append(&dbusArgs, v);
375 DBusPendingCall* pending;
376 if (!dbus_connection_send_with_reply(mConnection, msg.get(), &pending, -1)) {
377 throw ADBus::Exception(formatError(
"dbus_connection_send_with_reply failed"));
380 }(), dbus_pending_call_unref);
383 dbus_pending_call_block(pending.get());
386 msg.reset(dbus_pending_call_steal_reply(pending.get()));
388 if (dbus_message_get_type(msg.get()) == DBUS_MESSAGE_TYPE_ERROR) {
389 if (
auto e = dbus_message_get_error_name(msg.get())) {
390 throw ADBus::Exception(formatError(e));
392 throw ADBus::Exception(formatError(
"dbus replied unknown error"));
395 if constexpr (!std::is_void_v<Return>) {
396 if (!dbus_message_iter_init(msg.get(), &dbusArgs)) {
397 throw ADBus::Exception(formatError(
"dbus replied no arguments"));
399 return aui::dbus::iter_get<Return>(&dbusArgs);
403 template<aui::not_overloaded_lambda Callback>
404 std::function<void()> addSignalListener(aui::dbus::ObjectPath
object,
const AString& interface,
const AString& signal, Callback&& callback) {
405 return addListener([
object = std::move(
object),
408 callback = std::forward<Callback>(callback)](DBusMessage* msg) {
409 if (dbus_message_is_signal(msg, DBUS_INTERFACE_LOCAL,
"Disconnected")) {
410 return DBUS_HANDLER_RESULT_HANDLED;
413 if (!dbus_message_is_signal(msg, interface.c_str(), signal.c_str())) {
414 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
417 if (
object != std::string_view(dbus_message_get_path(msg))) {
418 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
421 using argz =
typename aui::lambda_info<Callback>::args;
422 if constexpr (std::tuple_size_v<argz> > 0) {
423 DBusMessageIter dbusArgs;
424 if (!dbus_message_iter_init(msg, &dbusArgs)) {
425 throw ADBus::Exception(
"dbus replied no arguments");
429 auto args = aui::tuple_visitor<argz>::for_each_make_tuple([&]<
typename T>() {
431 throw ADBus::Exception(
"too few arguments");
433 auto v = aui::dbus::iter_get<T>(&dbusArgs);
434 hasNext = dbus_message_iter_next(&dbusArgs);
438 std::apply(callback, std::move(args));
444 return DBUS_HANDLER_RESULT_HANDLED;
448 void processMessages();
451 struct RawMessageListener {
453 using Callback = std::function<DBusHandlerResult(DBusMessage* message)>;
456 std::list<RawMessageListener> mListeners;
457 aui::fast_pimpl<DBusError,
sizeof(
void*) * 3 + 20,
alignof(
void*)> mError;
458 DBusConnection* mConnection =
nullptr;
459 std::atomic_bool mProcessingScheduled =
false;
464 template <aui::invocable Callback>
465 void throwExceptionOnError(Callback&& callback);
466 std::function<void()> addListener(RawMessageListener::Callback listener);
467 static dbus_bool_t addWatch(DBusWatch* watch,
void* data);
469 static DBusHandlerResult listener(DBusConnection *connection,
470 DBusMessage *message,
471 void *user_data)
noexcept;
472 static void deleter(
void* userData)
noexcept;