2019-02-02 11:18:26 +03:00
|
|
|
/*
|
|
|
|
* linux_notify.c
|
|
|
|
* Copyright (C) 2019 Kovid Goyal <kovid at kovidgoyal.net>
|
|
|
|
*
|
|
|
|
* Distributed under terms of the GPL3 license.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "internal.h"
|
|
|
|
#include "linux_notify.h"
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
|
|
|
#define NOTIFICATIONS_SERVICE "org.freedesktop.Notifications"
|
|
|
|
#define NOTIFICATIONS_PATH "/org/freedesktop/Notifications"
|
|
|
|
#define NOTIFICATIONS_IFACE "org.freedesktop.Notifications"
|
|
|
|
|
|
|
|
static notification_id_type notification_id = 0;
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
notification_id_type next_id;
|
|
|
|
GLFWDBusnotificationcreatedfun callback;
|
|
|
|
void *data;
|
|
|
|
} NotificationCreatedData;
|
|
|
|
|
2019-02-03 10:50:07 +03:00
|
|
|
static GLFWDBusnotificationactivatedfun activated_handler = NULL;
|
|
|
|
|
|
|
|
void
|
|
|
|
glfw_dbus_set_user_notification_activated_handler(GLFWDBusnotificationactivatedfun handler) {
|
|
|
|
activated_handler = handler;
|
|
|
|
}
|
|
|
|
|
2019-02-02 11:18:26 +03:00
|
|
|
void
|
|
|
|
notification_created(DBusMessage *msg, const char* errmsg, void *data) {
|
|
|
|
if (errmsg) {
|
|
|
|
_glfwInputError(GLFW_PLATFORM_ERROR, "Notify: Failed to create notification error: %s", errmsg);
|
2019-02-24 07:49:19 +03:00
|
|
|
if (data) free(data);
|
2019-02-02 11:18:26 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
uint32_t notification_id;
|
|
|
|
if (!glfw_dbus_get_args(msg, "Failed to get Notification uid", DBUS_TYPE_UINT32, ¬ification_id, DBUS_TYPE_INVALID)) return;
|
|
|
|
NotificationCreatedData *ncd = (NotificationCreatedData*)data;
|
|
|
|
if (ncd->callback) ncd->callback(ncd->next_id, notification_id, ncd->data);
|
2019-02-24 07:49:19 +03:00
|
|
|
if (data) free(data);
|
2019-02-02 11:18:26 +03:00
|
|
|
}
|
|
|
|
|
2019-02-03 10:50:07 +03:00
|
|
|
static DBusHandlerResult
|
2019-07-01 08:12:07 +03:00
|
|
|
message_handler(DBusConnection *conn UNUSED, DBusMessage *msg, void *user_data UNUSED) {
|
2019-02-03 15:54:47 +03:00
|
|
|
/* printf("session_bus message_handler invoked interface: %s member: %s\n", dbus_message_get_interface(msg), dbus_message_get_member(msg)); */
|
|
|
|
if (dbus_message_is_signal(msg, NOTIFICATIONS_IFACE, "ActionInvoked")) {
|
|
|
|
uint32_t notification_id;
|
|
|
|
const char *action;
|
2019-02-03 17:11:19 +03:00
|
|
|
if (glfw_dbus_get_args(msg, "Failed to get args from ActionInvoked notification signal",
|
2019-02-03 15:54:47 +03:00
|
|
|
DBUS_TYPE_UINT32, ¬ification_id, DBUS_TYPE_STRING, &action, DBUS_TYPE_INVALID)) {
|
|
|
|
if (activated_handler) {
|
|
|
|
activated_handler(notification_id, action);
|
|
|
|
return DBUS_HANDLER_RESULT_HANDLED;
|
2019-02-03 10:50:07 +03:00
|
|
|
}
|
2019-02-03 15:54:47 +03:00
|
|
|
}
|
|
|
|
|
2019-02-03 10:50:07 +03:00
|
|
|
}
|
|
|
|
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
|
|
|
}
|
|
|
|
|
2019-02-02 11:18:26 +03:00
|
|
|
notification_id_type
|
2019-02-03 11:23:26 +03:00
|
|
|
glfw_dbus_send_user_notification(const char *app_name, const char* icon, const char *summary, const char *body, const char* action_name, int32_t timeout, GLFWDBusnotificationcreatedfun callback, void *user_data) {
|
2019-02-02 11:18:26 +03:00
|
|
|
DBusConnection *session_bus = glfw_dbus_session_bus();
|
2019-02-03 10:50:07 +03:00
|
|
|
static DBusConnection *added_signal_match = NULL;
|
2019-02-02 11:18:26 +03:00
|
|
|
if (!session_bus) return 0;
|
2019-02-03 10:50:07 +03:00
|
|
|
if (added_signal_match != session_bus) {
|
2019-02-03 15:54:47 +03:00
|
|
|
dbus_bus_add_match(session_bus, "type='signal',interface='" NOTIFICATIONS_IFACE "',member='ActionInvoked'", NULL);
|
|
|
|
dbus_connection_add_filter(session_bus, message_handler, NULL, NULL);
|
2019-02-03 13:28:29 +03:00
|
|
|
added_signal_match = session_bus;
|
2019-02-03 10:50:07 +03:00
|
|
|
}
|
2019-02-02 11:18:26 +03:00
|
|
|
NotificationCreatedData *data = malloc(sizeof(NotificationCreatedData));
|
2019-02-24 16:48:54 +03:00
|
|
|
if (!data) return 0;
|
2019-02-02 11:18:26 +03:00
|
|
|
data->next_id = ++notification_id;
|
|
|
|
data->callback = callback; data->data = user_data;
|
|
|
|
if (!data->next_id) data->next_id = ++notification_id;
|
|
|
|
uint32_t replaces_id = 0;
|
|
|
|
|
|
|
|
DBusMessage *msg = dbus_message_new_method_call(NOTIFICATIONS_SERVICE, NOTIFICATIONS_PATH, NOTIFICATIONS_IFACE, "Notify");
|
2019-02-24 07:49:19 +03:00
|
|
|
if (!msg) { free(data); return 0; }
|
2019-02-02 11:18:26 +03:00
|
|
|
DBusMessageIter args, array;
|
|
|
|
dbus_message_iter_init_append(msg, &args);
|
2019-02-24 07:49:19 +03:00
|
|
|
#define OOMMSG { free(data); data = NULL; dbus_message_unref(msg); _glfwInputError(GLFW_PLATFORM_ERROR, "%s", "Out of memory allocating DBUS message for notification\n"); return 0; }
|
2019-02-02 11:18:26 +03:00
|
|
|
#define APPEND(type, val) { if (!dbus_message_iter_append_basic(&args, type, val)) OOMMSG }
|
|
|
|
APPEND(DBUS_TYPE_STRING, &app_name)
|
|
|
|
APPEND(DBUS_TYPE_UINT32, &replaces_id)
|
|
|
|
APPEND(DBUS_TYPE_STRING, &icon)
|
|
|
|
APPEND(DBUS_TYPE_STRING, &summary)
|
|
|
|
APPEND(DBUS_TYPE_STRING, &body)
|
|
|
|
if (!dbus_message_iter_open_container(&args, DBUS_TYPE_ARRAY, "s", &array)) OOMMSG;
|
2019-02-03 11:23:26 +03:00
|
|
|
if (action_name) {
|
|
|
|
static const char* default_action = "default";
|
|
|
|
dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING, &default_action);
|
|
|
|
dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING, &action_name);
|
|
|
|
}
|
2019-02-02 11:18:26 +03:00
|
|
|
if (!dbus_message_iter_close_container(&args, &array)) OOMMSG;
|
|
|
|
if (!dbus_message_iter_open_container(&args, DBUS_TYPE_ARRAY, "{sv}", &array)) OOMMSG;
|
|
|
|
if (!dbus_message_iter_close_container(&args, &array)) OOMMSG;
|
|
|
|
APPEND(DBUS_TYPE_INT32, &timeout)
|
|
|
|
#undef OOMMSG
|
|
|
|
#undef APPEND
|
|
|
|
if (!call_method_with_msg(session_bus, msg, 5000, notification_created, data)) return 0;
|
|
|
|
return data->next_id;
|
|
|
|
}
|