mirror of
https://github.com/robbert-vdh/yabridge.git
synced 2026-05-07 03:50:11 +02:00
Drop notify-send and use libdbus-1 directly
This is mostly useful for more obscure distros that don't ship notify-send together with libnotify.
This commit is contained in:
@@ -34,21 +34,34 @@ std::atomic<void*> libdbus_handle = nullptr;
|
||||
std::mutex libdbus_mutex;
|
||||
|
||||
// We'll fetch all of these functions at runtime when send a first notification
|
||||
decltype(dbus_connection_unref)* libdbus_connection_unref = nullptr;
|
||||
decltype(dbus_bus_get)* libdbus_bus_get = nullptr;
|
||||
decltype(dbus_error_free)* libdbus_error_free = nullptr;
|
||||
decltype(dbus_error_init)* libdbus_error_init = nullptr;
|
||||
decltype(dbus_error_is_set)* libdbus_error_is_set = nullptr;
|
||||
decltype(dbus_connection_set_exit_on_disconnect)*
|
||||
libdbus_connection_set_exit_on_disconnect = nullptr;
|
||||
#define LIBDBUS_FUNCTIONS \
|
||||
X(dbus_bus_get) \
|
||||
X(dbus_connection_flush) \
|
||||
X(dbus_connection_send) \
|
||||
X(dbus_connection_set_exit_on_disconnect) \
|
||||
X(dbus_connection_unref) \
|
||||
X(dbus_error_free) \
|
||||
X(dbus_error_init) \
|
||||
X(dbus_error_is_set) \
|
||||
X(dbus_message_get_serial) \
|
||||
X(dbus_message_iter_append_basic) \
|
||||
X(dbus_message_iter_close_container) \
|
||||
X(dbus_message_iter_init_append) \
|
||||
X(dbus_message_iter_open_container) \
|
||||
X(dbus_message_new_method_call) \
|
||||
X(dbus_message_unref)
|
||||
|
||||
#define X(name) decltype(name)* lib##name = nullptr;
|
||||
LIBDBUS_FUNCTIONS
|
||||
#undef X
|
||||
|
||||
std::unique_ptr<DBusConnection, void (*)(DBusConnection*)> libdbus_connection(
|
||||
nullptr,
|
||||
libdbus_connection_unref);
|
||||
|
||||
/**
|
||||
* Try to set up DBus. Returns `false` if a function could not be resolved or if
|
||||
* we could not connect to the DBus session.
|
||||
* Try to set up D-Bus. Returns `false` if a function could not be resolved or
|
||||
* if we could not connect to the D-Bus session.
|
||||
*/
|
||||
bool setup_libdbus() {
|
||||
// If this function is called from two threads at the same time, then we can
|
||||
@@ -68,7 +81,7 @@ bool setup_libdbus() {
|
||||
return false;
|
||||
}
|
||||
|
||||
#define LOAD_FUNCTION(name) \
|
||||
#define X(name) \
|
||||
do { \
|
||||
lib##name = \
|
||||
reinterpret_cast<decltype(lib##name)>(dlsym(handle, #name)); \
|
||||
@@ -78,18 +91,13 @@ bool setup_libdbus() {
|
||||
"', not sending desktop notifications"); \
|
||||
return false; \
|
||||
} \
|
||||
} while (false)
|
||||
} while (false);
|
||||
|
||||
LOAD_FUNCTION(dbus_connection_unref);
|
||||
LOAD_FUNCTION(dbus_bus_get);
|
||||
LOAD_FUNCTION(dbus_error_free);
|
||||
LOAD_FUNCTION(dbus_error_init);
|
||||
LOAD_FUNCTION(dbus_error_is_set);
|
||||
LOAD_FUNCTION(dbus_connection_set_exit_on_disconnect);
|
||||
LIBDBUS_FUNCTIONS
|
||||
|
||||
#undef LOAD_FUNCTION
|
||||
#undef X
|
||||
|
||||
// With every function ready, we can try connecting to the DBus interface
|
||||
// With every function ready, we can try connecting to the D-Bus interface
|
||||
DBusError error;
|
||||
libdbus_error_init(&error);
|
||||
|
||||
@@ -97,7 +105,7 @@ bool setup_libdbus() {
|
||||
libdbus_bus_get(DBusBusType::DBUS_BUS_SESSION, &error));
|
||||
if (libdbus_error_is_set(&error)) {
|
||||
assert(error.message);
|
||||
logger.log("Could not connect to DBus session bus: " +
|
||||
logger.log("Could not connect to D-Bus session bus: " +
|
||||
std::string(error.message));
|
||||
libdbus_error_free(&error);
|
||||
|
||||
@@ -119,7 +127,7 @@ bool setup_libdbus() {
|
||||
bool send_notification(const std::string& title,
|
||||
const std::string body,
|
||||
std::optional<ghc::filesystem::path> origin) {
|
||||
// The first time this function is called we'll need to set up the DBus
|
||||
// The first time this function is called we'll need to set up the D-Bus
|
||||
// interface. Previously yabridge relied on notify-send, but some distros
|
||||
// don't install that by default.
|
||||
if (!libdbus_handle && !setup_libdbus()) {
|
||||
@@ -151,20 +159,65 @@ bool send_notification(const std::string& title,
|
||||
}
|
||||
}
|
||||
|
||||
Process process("notify-send");
|
||||
process.arg("--urgency=normal");
|
||||
process.arg("--app-name=yabridge");
|
||||
process.arg(title);
|
||||
process.arg(formatted_body.str());
|
||||
// Actually sending the notification is done directly using the D-Bus API.
|
||||
std::unique_ptr<DBusMessage, void (*)(DBusMessage*)> message(
|
||||
libdbus_message_new_method_call(
|
||||
"org.freedesktop.Notifications", "/org/freedesktop/Notifications",
|
||||
"org.freedesktop.Notifications", "Notify"),
|
||||
libdbus_message_unref);
|
||||
assert(message);
|
||||
|
||||
// We will have printed the message to the terminal anyways, so if the user
|
||||
// doesn't have libnotify installed we'll just fail silently
|
||||
const auto result = process.spawn_get_status();
|
||||
return std::visit(
|
||||
overload{
|
||||
[](int status) -> bool { return status == EXIT_SUCCESS; },
|
||||
[](const Process::CommandNotFound&) -> bool { return false; },
|
||||
[](const std::error_code&) -> bool { return false; },
|
||||
},
|
||||
result);
|
||||
// Can't use `dbus_message_append_args` because that doesn't support
|
||||
// dictionaries
|
||||
DBusMessageIter iter;
|
||||
libdbus_message_iter_init_append(message.get(), &iter);
|
||||
|
||||
const char* app_name = "yabridge";
|
||||
assert(
|
||||
libdbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &app_name));
|
||||
|
||||
// TODO: Add the replacing thing, see if that helps prevent notifications
|
||||
// from stacking up
|
||||
const dbus_uint32_t replaces_id = 0;
|
||||
assert(libdbus_message_iter_append_basic(&iter, DBUS_TYPE_UINT32,
|
||||
&replaces_id));
|
||||
|
||||
const char* app_icon = "";
|
||||
assert(
|
||||
libdbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &app_icon));
|
||||
|
||||
const char* title_cstr = title.c_str();
|
||||
assert(libdbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING,
|
||||
&title_cstr));
|
||||
|
||||
const std::string formatted_body_str = formatted_body.str();
|
||||
const char* formatted_body_cstr = formatted_body_str.c_str();
|
||||
assert(libdbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING,
|
||||
&formatted_body_cstr));
|
||||
|
||||
// Our actions array is empty
|
||||
DBusMessageIter array_iter;
|
||||
assert(libdbus_message_iter_open_container(
|
||||
&iter, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING_AS_STRING, &array_iter));
|
||||
assert(libdbus_message_iter_close_container(&iter, &array_iter));
|
||||
|
||||
// We also don't have any hints, but we can't use the simple
|
||||
// `libdbus_message_append_args` API because we can't use it to add an empty
|
||||
// hints dictionary
|
||||
assert(libdbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}",
|
||||
&array_iter));
|
||||
assert(libdbus_message_iter_close_container(&iter, &array_iter));
|
||||
|
||||
// -1 is an implementation specific default duration
|
||||
const dbus_int32_t expiry_timeout = -1;
|
||||
assert(libdbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32,
|
||||
&expiry_timeout));
|
||||
|
||||
// And after all of that we can finally send the actual notification
|
||||
dbus_uint32_t message_serial = libdbus_message_get_serial(message.get());
|
||||
const bool result = libdbus_connection_send(libdbus_connection.get(),
|
||||
message.get(), &message_serial);
|
||||
libdbus_connection_flush(libdbus_connection.get());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -22,14 +22,11 @@
|
||||
#include <ghc/filesystem.hpp>
|
||||
#include <optional>
|
||||
|
||||
// TODO: At some point, provide an alternative to notify-send by dlopen()-ing
|
||||
// libdbus instead. Some more obscure distros won't have notify-send
|
||||
// available.
|
||||
|
||||
/**
|
||||
* Send a desktop notification using `notify-send`. Used for diagnostics when a
|
||||
* plugin fails to load since the user may not be checking the output in a
|
||||
* terminal.
|
||||
* Send a desktop notification using the D-Bus notifications protocol
|
||||
* (<https://specifications.freedesktop.org/notification-spec/notification-spec-latest.html>).
|
||||
* Used for diagnostics when a plugin fails to load since the user may not be
|
||||
* checking the output in a terminal.
|
||||
*
|
||||
* @param title The title (or technically, summary) of the notification.
|
||||
* @param body The message to display. This can contain line feeds, and it any
|
||||
@@ -40,7 +37,7 @@
|
||||
* user can more easily navigate to the plugin's path.
|
||||
*
|
||||
* @return Whether the notification was sent. This will be false if
|
||||
* `notify-send` is not available.
|
||||
* `libdbus-1.so` is not available.
|
||||
*/
|
||||
bool send_notification(const std::string& title,
|
||||
const std::string body,
|
||||
|
||||
Reference in New Issue
Block a user