Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
136 changes: 35 additions & 101 deletions src/tray_linux.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
#include <memory>
#include <mutex>
#include <string>
#include <thread>
#include <utility>

// lib includes
Expand All @@ -22,36 +21,17 @@

namespace tray_linux {
/**
* Notification element struct
* Currently shown notification object
*/
struct notification_data {
/**
* @brief Notification object
*/
NotifyNotification *obj = nullptr;
/**
* @brief Notification callback
*/
void (*cb)() = nullptr;
/**
* @brief Notification shown indicator
*/
bool shown = false;
/**
* @brief Notification mutex for async thread synchronization
*/
std::mutex mutex;
};

NotifyNotification *notification_current = nullptr; // NOSONAR(cpp:S5421) - mutable state, not const
/**
* Currently shown notifications
* Currently shown notification callback
*/
std::vector<std::shared_ptr<notification_data>> notifications; // NOSONAR(cpp:S5421) - mutable state, not const
void (*notification_current_callback)() = nullptr; // NOSONAR(cpp:S5421) - mutable state, not const
/**
* Lock for currently shown notifications vector
* Lock for currently shown notification/callback
*/
std::mutex notifications_mutex; // NOSONAR(cpp:S5421) - mutable state, not const

std::mutex notification_mutex; // NOSONAR(cpp:S5421) - mutable state, not const
/**
* QtTrayMenu instance
*/
Expand All @@ -62,64 +42,34 @@ namespace tray_linux {
void (*log_callback)(int, const char *) = nullptr; // NOSONAR(cpp:S5421) - mutable state, not const

/**
* @brief Show notification asynchronously with timeout to avoid Dbus lockups
* @param notification - Tray notification to show
* @param timeout - optional timeout for async run in ms (default: 1000)
* @return true if notification was successfully shown
*/
bool async_tray_notification_show_(const std::shared_ptr<notification_data> &notification, int timeout = 1000) {
std::thread t([notification]() { // NOSONAR(cpp:S6168) - jthread is only available on C++20 onwards
std::scoped_lock lock(notification->mutex);
if (notification->obj != nullptr && NOTIFY_IS_NOTIFICATION(notification->obj) && notify_notification_show(notification->obj, nullptr)) {
notification->shown = true;
}
});
t.detach(); // NOSONAR(cpp:S5962)
while (!notification->shown && timeout > 0) {
std::this_thread::sleep_for(std::chrono::milliseconds(10));
timeout -= 10;
}
return notification->shown;
}

/**
* @brief Acknowledge notification asynchronously with timeout to avoid Dbus lockups
* @param notification - Tray notification to close
* @param timeout - optional timeout for async run in ms (default: 1000)
* @return true if notification was successfully closed
* @brief Initialize notifications
* @param app_name application name for notifications
* @return true if successful
*/
bool async_tray_notification_acknowledge_(const std::shared_ptr<notification_data> &notification, int timeout = 1000) {
std::thread t([notification]() { // NOSONAR(cpp:S6168) - jthread is only available on C++20 onwards
std::scoped_lock lock(notification->mutex);
if (notification->obj != nullptr && NOTIFY_IS_NOTIFICATION(notification->obj) && notify_notification_close(notification->obj, nullptr)) {
notification->shown = false;
g_object_unref(G_OBJECT(notification->obj));
notification->obj = nullptr;
notification->cb = nullptr;
}
});
t.detach(); // NOSONAR(cpp:S5962)
while (notification->obj != nullptr && timeout > 0) {
std::this_thread::sleep_for(std::chrono::milliseconds(10));
timeout -= 10;
bool init_notify(const char *app_name) {
if (!notify_is_initted()) {
std::scoped_lock lock(notification_mutex);
return notify_init(app_name);
}
return notification->obj == nullptr;
return true; // Already initialized, so init was successful
}

/**
* @brief Acknowledge/click current notifications
* @brief Acknowledge/click current notification
* @param run_callback - Run notification callback when acknowledging
*/
void acknowledge_notifications(bool run_callback = false) {
void acknowledge_notification(const bool run_callback = false) {
if (notify_is_initted()) {
std::scoped_lock lock(notifications_mutex);
for (auto notification : notifications) {
if (run_callback && notification->cb != nullptr) {
notification->cb();
std::scoped_lock lock(notification_mutex);
if (notification_current != nullptr && NOTIFY_IS_NOTIFICATION(notification_current)) {
if (run_callback && notification_current_callback != nullptr) {
notification_current_callback();
}
async_tray_notification_acknowledge_(notification);
notify_notification_close(notification_current, nullptr);
g_object_unref(G_OBJECT(notification_current));
notification_current = nullptr;
notification_current_callback = nullptr;
}
notifications.clear();
} else if (qt_tray_menu != nullptr && QtTrayMenu::supportsMessages()) {
qt_tray_menu->clickMessage();
}
Expand All @@ -135,24 +85,22 @@ namespace tray_linux {
}
// Try to notify using libnotify
if (notify_is_initted()) {
std::scoped_lock lock(notifications_mutex);
if (!notifications.empty()) {
acknowledge_notifications();
if (notification_current != nullptr) {
acknowledge_notification();
}
std::scoped_lock lock(notification_mutex);
std::filesystem::path notification_icon = tray->notification_icon != nullptr ? tray->notification_icon : tray->icon;
if (std::filesystem::exists(notification_icon)) {
// Use absolute path for filesystem icon files, not a relative one
notification_icon = std::filesystem::absolute(notification_icon);
}
auto notification = std::make_shared<struct notification_data>();
notification->obj = notify_notification_new(tray->notification_title, tray->notification_text, notification_icon.c_str());
if (notification->obj != nullptr && NOTIFY_IS_NOTIFICATION(notification->obj)) {
notification_current = notify_notification_new(tray->notification_title, tray->notification_text, notification_icon.c_str());
if (notification_current != nullptr && NOTIFY_IS_NOTIFICATION(notification_current)) {
if (tray->notification_cb != nullptr) {
notification->cb = tray->notification_cb;
notify_notification_add_action(notification->obj, "default", "Default", NOTIFY_ACTION_CALLBACK(tray->notification_cb), nullptr, nullptr);
notification_current_callback = tray->notification_cb;
notify_notification_add_action(notification_current, "default", "Default", NOTIFY_ACTION_CALLBACK(tray->notification_cb), nullptr, nullptr);
}
notifications.emplace_back(notification);
if (async_tray_notification_show_(notification)) {
if (notify_notification_show(notification_current, nullptr)) {
return;
}
}
Expand All @@ -163,27 +111,13 @@ namespace tray_linux {
}
}

/**
* @brief Initialize notifications
* @param app_name application name for notifications
* @return true if successful
*/
bool init_notify(const char *app_name) {
if (!notify_is_initted()) {
if (!notifications.empty()) {
acknowledge_notifications();
}
return notify_init(app_name);
}
return true; // Already initialized, so init was successful
}

/**
* @brief Uninitialize notifications
*/
void uninit_notify() {
if (notify_is_initted()) {
acknowledge_notifications();
acknowledge_notification();
std::scoped_lock lock(notification_mutex);
notify_uninit();
}
}
Expand Down Expand Up @@ -311,6 +245,6 @@ extern "C" {
}

void tray_simulate_notification_click(void) {
tray_linux::acknowledge_notifications(true);
tray_linux::acknowledge_notification(true);
}
} // extern "C"
Loading