diff --git a/legacy/elementary/src/lib/Makefile.am b/legacy/elementary/src/lib/Makefile.am index 424095ee92..20f6521b83 100644 --- a/legacy/elementary/src/lib/Makefile.am +++ b/legacy/elementary/src/lib/Makefile.am @@ -348,6 +348,8 @@ elm_systray_eo.h \ elm_systray_common.h \ elm_systray_watcher.h \ elm_sys_notify.h \ +elm_sys_notify.eo.h \ +elm_sys_notify_dbus.eo.h \ elm_table.h \ elm_table_eo.h \ elm_table_legacy.h \ @@ -477,7 +479,9 @@ elm_spinner.c \ elm_store.c \ elm_systray.c \ elm_systray_watcher.c \ +elm_sys_notify_interface.c \ elm_sys_notify.c \ +elm_sys_notify_dbus.c \ elm_table.c \ elm_theme.c \ elm_thumb.c \ @@ -594,6 +598,9 @@ elm_separator.eo \ elm_slider.eo \ elm_slideshow.eo \ elm_spinner.eo \ +elm_sys_notify_interface.eo \ +elm_sys_notify.eo \ +elm_sys_notify_dbus.eo \ elm_systray.eo \ elm_table.eo \ elm_thumb.eo \ diff --git a/legacy/elementary/src/lib/elm_sys_notify.c b/legacy/elementary/src/lib/elm_sys_notify.c index 23c936f2f0..87536b806b 100644 --- a/legacy/elementary/src/lib/elm_sys_notify.c +++ b/legacy/elementary/src/lib/elm_sys_notify.c @@ -6,372 +6,291 @@ #include "elm_priv.h" -#define OBJ "/org/freedesktop/Notifications" -#define BUS "org.freedesktop.Notifications" -#define INTERFACE "org.freedesktop.Notifications" +#include "elm_sys_notify_dbus.eo.h" +#include "elm_sys_notify_dbus.eo.legacy.h" + +#define MY_CLASS ELM_SYS_NOTIFY_CLASS + +#define MY_CLASS_NAME "Elm_Sys_Notify" +#define MY_CLASS_NAME_LEGACY "elm_sys_notify" EAPI int ELM_EVENT_SYS_NOTIFY_NOTIFICATION_CLOSED = 0; EAPI int ELM_EVENT_SYS_NOTIFY_ACTION_INVOKED = 0; -static Eina_Bool _elm_need_sys_notify = EINA_FALSE; +typedef const Eo_Class *(*Class_Get_Func)(void); -static Eldbus_Connection *_elm_sysnotif_conn = NULL; -static Eldbus_Object *_elm_sysnotif_obj = NULL; -static Eldbus_Proxy *_elm_sysnotif_proxy = NULL; +static Elm_Sys_Notify *_singleton = NULL; -static Eina_Bool _has_markup = EINA_FALSE; +/* + * Registration of notification servers is done UNIQUELY + * in the two structures below. + * 1) ALWAYS add a SRV_XXX before __SRV_LAST + * 2) copy the #if ... #else ... #endif with the appropriate class + * getter (generated by Eolian) or NULL when unsupported + * + * The rest of the code relies on the Srv enum and _class_getters + * to register/unregister notification servers. + */ -typedef struct _Elm_Sys_Notify_Send_Data +typedef enum { - Elm_Sys_Notify_Send_Cb cb; - const void *data; -} Elm_Sys_Notify_Send_Data; + SRV_DBUS = 0, + __SRV_LAST /* Sentinel */ +} Srv; -static void -_elm_sys_notify_marshal_dict_byte(Eldbus_Message_Iter *array, - const char *key, - const char value) +static Class_Get_Func _class_getters[__SRV_LAST] = { - Eldbus_Message_Iter *var, *entry; +#ifdef ELM_SYS_NOTIFY_DBUS_CLASS + [SRV_DBUS] = elm_sys_notify_dbus_class_get +#else + [SRV_DBUS] = NULL +#endif +}; - eldbus_message_iter_arguments_append(array, "{sv}", &entry); - eldbus_message_iter_basic_append(entry, 's', key); - var = eldbus_message_iter_container_new(entry, 'v', "y"); - eldbus_message_iter_basic_append(var, 'y', value); - eldbus_message_iter_container_close(entry, var); - eldbus_message_iter_container_close(array, entry); + +typedef struct +{ + Eo *servers[__SRV_LAST]; +} Elm_Sys_Notify_Data; + +/*============================================================================* + * Constructor/Destructor - Singleton setup * + *============================================================================*/ + +EOLIAN static Eo_Base * +_elm_sys_notify_eo_base_constructor(Eo *obj, + Elm_Sys_Notify_Data *sd EINA_UNUSED) +{ + if (_singleton != NULL) + { + ERR("Attempted to create another system notification manager"); + return NULL; + } + + obj = eo_do_super_ret(obj, MY_CLASS, obj, eo_constructor()); + _singleton = obj; + + return obj; } -static void -_elm_sys_notify_marshal_dict_string(Eldbus_Message_Iter *array, - const char *key, - const char *value) +EOLIAN static void +_elm_sys_notify_eo_base_destructor(Eo *obj, + Elm_Sys_Notify_Data *sd EINA_UNUSED) { - Eldbus_Message_Iter *var, *entry; - - eldbus_message_iter_arguments_append(array, "{sv}", &entry); - eldbus_message_iter_basic_append(entry, 's', key); - - var = eldbus_message_iter_container_new(entry, 'v', "s"); - eldbus_message_iter_basic_append(var, 's', value); - eldbus_message_iter_container_close(entry, var); - eldbus_message_iter_container_close(array, entry); + eo_do_super(obj, MY_CLASS, eo_destructor()); + _singleton = NULL; } -static void -_get_capabilities_cb(void *data EINA_UNUSED, - const Eldbus_Message *msg, - Eldbus_Pending *pending EINA_UNUSED) + +/*============================================================================* + * Notification Interface * + *============================================================================*/ + +EOLIAN static void +_elm_sys_notify_elm_sys_notify_interface_send(const Eo *obj EINA_UNUSED, + Elm_Sys_Notify_Data *sd, + unsigned int replaces_id, + const char *icon, + const char *summary, + const char *body, + Elm_Sys_Notify_Urgency urgency, + int timeout, + Elm_Sys_Notify_Send_Cb cb, + const void *cb_data) { - char *val; - Eldbus_Message_Iter *arr; + Srv i; - if (eldbus_message_error_get(msg, NULL, NULL) || - !eldbus_message_arguments_get(msg, "as", &arr)) goto end; - - while (eldbus_message_iter_get_and_next(arr, 's', &val)) - if (!strcmp(val, "body-markup")) - { - _has_markup = EINA_TRUE; - return; - } - -end: - _has_markup = EINA_FALSE; + /* Propagate to all registered servers */ + for (i = SRV_DBUS; i < __SRV_LAST; ++i) + if (sd->servers[i]) + eo_do(sd->servers[i], + elm_obj_sys_notify_interface_send(replaces_id, + icon, summary, body, + urgency, timeout, + cb, cb_data)); } +EOLIAN static void +_elm_sys_notify_elm_sys_notify_interface_simple_send(const Eo *obj EINA_UNUSED, + Elm_Sys_Notify_Data *sd, + const char *icon, + const char *summary, + const char *body) +{ + Srv i; + + /* Propagate to all registered servers */ + for (i = SRV_DBUS; i < __SRV_LAST; ++i) + if (sd->servers[i]) + eo_do(sd->servers[i], + elm_obj_sys_notify_interface_simple_send(icon, summary, body)); +} + +EOLIAN static void +_elm_sys_notify_elm_sys_notify_interface_close(const Eo *obj EINA_UNUSED, + Elm_Sys_Notify_Data *sd, + unsigned int id) +{ + Srv i; + + /* Propagate to all registered servers */ + for (i = SRV_DBUS; i < __SRV_LAST; ++i) + if (sd->servers[i]) + eo_do(sd->servers[i], + elm_obj_sys_notify_interface_close(id)); +} + + +/*============================================================================* + * Methods * + *============================================================================*/ + +EOLIAN static Eina_Bool +_elm_sys_notify_servers_set(Eo *obj EINA_UNUSED, + Elm_Sys_Notify_Data *sd, + Elm_Sys_Notify_Server servers) +{ + Srv i; + Class_Get_Func class_get; + + /* Update the notification servers */ + for (i = SRV_DBUS; i < __SRV_LAST; ++i) + { + if (sd->servers[i]) + { + /* Delete if server type is not provided */ + if (!(servers & (1 << i))) + eo_del(sd->servers[i]); + } + else + { + /* If server is required, create when nonexistant */ + if (servers & (1 << i)) + { + class_get = _class_getters[i]; + if (!class_get) + { + CRI("Unsupported notification server"); + return EINA_FALSE; + } + + sd->servers[i] = eo_add(class_get(), NULL); + if (EINA_UNLIKELY(!(sd->servers[i]))) + { + CRI("Failed to create notification server"); + return EINA_FALSE; + } + } + } + } + + return EINA_TRUE; +} + +EOLIAN static Elm_Sys_Notify_Server +_elm_sys_notify_servers_get(Eo *obj EINA_UNUSED, + Elm_Sys_Notify_Data *sd) +{ + Elm_Sys_Notify_Server servers = ELM_SYS_NOTIFY_SERVER_NONE; + Srv i; + + for (i = SRV_DBUS; i < __SRV_LAST; ++i) + if (sd->servers[i]) + servers |= (1 << i); + + return servers; +} + +EOLIAN static Elm_Sys_Notify * +_elm_sys_notify_singleton_get(Eo *obj EINA_UNUSED, + void *sd EINA_UNUSED) +{ + if (!_singleton) + _singleton = eo_add(MY_CLASS, NULL); + return _singleton; +} + +EOLIAN static void +_elm_sys_notify_class_constructor(Eo_Class *klass EINA_UNUSED) +{ + ELM_EVENT_SYS_NOTIFY_NOTIFICATION_CLOSED = ecore_event_type_new(); + ELM_EVENT_SYS_NOTIFY_ACTION_INVOKED = ecore_event_type_new(); +} + +/*============================================================================* + * Legacy API * + *============================================================================*/ + void -_elm_sys_notify_capabilities_get(void) +_elm_unneed_sys_notify(void) { - EINA_SAFETY_ON_NULL_RETURN(_elm_sysnotif_proxy); - - if (!eldbus_proxy_call(_elm_sysnotif_proxy, "GetCapabilities", - _get_capabilities_cb, NULL, -1, "")) - ERR("Error sending message: "INTERFACE".GetCapabilities."); -} - -static void -_close_notification_cb(void *data EINA_UNUSED, - const Eldbus_Message *msg, - Eldbus_Pending *pending EINA_UNUSED) -{ - const char *errname, *errmsg; - - if (eldbus_message_error_get(msg, &errname, &errmsg)) - { - if (errmsg && errmsg[0] == '\0') - INF("Notification no longer exists."); - else - ERR("Eldbus Error: %s %s", errname, errmsg); - } -} - -EAPI void -elm_sys_notify_close(unsigned int id) -{ - EINA_SAFETY_ON_FALSE_RETURN(_elm_need_sys_notify); - EINA_SAFETY_ON_NULL_RETURN(_elm_sysnotif_proxy); - - if (!eldbus_proxy_call(_elm_sysnotif_proxy, "CloseNotification", - _close_notification_cb, NULL, -1, "u", id)) - ERR("Error sending message: "INTERFACE".CloseNotification."); -} - -static void -_notify_cb(void *data, - const Eldbus_Message *msg, - Eldbus_Pending *pending EINA_UNUSED) -{ - const char *errname, *errmsg; - Elm_Sys_Notify_Send_Data *d = data; - unsigned int id = 0; - - if (eldbus_message_error_get(msg, &errname, &errmsg)) - ERR("Error: %s %s", errname, errmsg); - else if (!eldbus_message_arguments_get(msg, "u", &id)) - { - ERR("Error getting return values of "INTERFACE".Notify."); - id = 0; - } - - if (d->cb) d->cb((void *)d->data, id); - free(d); -} - -EAPI void -elm_sys_notify_send(unsigned int replaces_id, const char *icon, - const char *summary, const char *body, - Elm_Sys_Notify_Urgency urgency, int timeout, - Elm_Sys_Notify_Send_Cb cb, const void *cb_data) -{ - Eldbus_Message *msg; - Eldbus_Message_Iter *iter, *actions, *hints; - Elm_Sys_Notify_Send_Data *data; - char *body_free = NULL; - char *desk_free = NULL; - const char *deskentry = elm_app_desktop_entry_get(); - const char *appname = elm_app_name_get(); - - EINA_SAFETY_ON_FALSE_RETURN(_elm_need_sys_notify); - EINA_SAFETY_ON_NULL_RETURN(_elm_sysnotif_proxy); - - data = malloc(sizeof(Elm_Sys_Notify_Send_Data)); - EINA_SAFETY_ON_NULL_GOTO(data, error); - data->cb = cb; - data->data = cb_data; - - if (!icon) icon = ""; - if (!summary) summary = ""; - if (!body) - body = ""; - else if (!_has_markup) - body = body_free = elm_entry_markup_to_utf8(body); - - msg = eldbus_proxy_method_call_new(_elm_sysnotif_proxy, "Notify"); - - iter = eldbus_message_iter_get(msg); - eldbus_message_iter_arguments_append(iter, "susssas", appname, replaces_id, - icon, summary, body, &actions); - /* actions */ - eldbus_message_iter_container_close(iter, actions); - - /* hints */ - eldbus_message_iter_arguments_append(iter, "a{sv}", &hints); - _elm_sys_notify_marshal_dict_byte(hints, "urgency", (char) urgency); - - if (strcmp(deskentry, "")) - { - deskentry = ecore_file_file_get(deskentry); - deskentry = desk_free = ecore_file_strip_ext(deskentry); - _elm_sys_notify_marshal_dict_string(hints, "desktop_entry", deskentry); - } - eldbus_message_iter_container_close(iter, hints); - - /* timeout */ - eldbus_message_iter_arguments_append(iter, "i", timeout); - - eldbus_proxy_send(_elm_sysnotif_proxy, msg, _notify_cb, data, -1); - free(desk_free); - free(body_free); - return; - -error: - if (cb) cb((void *)cb_data, 0); -} - -static void -_on_notification_closed(void *data EINA_UNUSED, - const Eldbus_Message *msg) -{ - const char *errname; - const char *errmsg; - Elm_Sys_Notify_Notification_Closed *d; - - if (eldbus_message_error_get(msg, &errname, &errmsg)) - { - ERR("Eldbus Error: %s %s", errname, errmsg); - return; - } - - d = malloc(sizeof(*d)); - - if (!eldbus_message_arguments_get(msg, "uu", &(d->id), &(d->reason))) - { - ERR("Error processing signal: "INTERFACE".NotificationClosed."); - goto cleanup; - } - - if (!ecore_event_add(ELM_EVENT_SYS_NOTIFY_NOTIFICATION_CLOSED, d, - NULL, NULL)) goto cleanup; - - return; - -cleanup: - free(d); -} - -static void -_ev_action_invoked_free(void *data EINA_UNUSED, - void *ev_data) -{ - Elm_Sys_Notify_Action_Invoked *d = ev_data; - - free(d->action_key); - free(d); -} - -static void -_on_action_invoked(void *data EINA_UNUSED, - const Eldbus_Message *msg) -{ - const char *errname; - const char *aux; - - Elm_Sys_Notify_Action_Invoked *d; - - if (eldbus_message_error_get(msg, &errname, &aux)) - { - ERR("Eldbus Error: %s %s", errname, aux); - return; - } - - d = calloc(1, sizeof(*d)); - if (!d) - { - ERR("Fail to allocate memory"); - return; - } - - if (!eldbus_message_arguments_get(msg, "us", &(d->id), &aux)) - { - ERR("Error processing signal: "INTERFACE".ActionInvoked."); - goto cleanup; - } - - d->action_key = strdup(aux); - - if (!ecore_event_add(ELM_EVENT_SYS_NOTIFY_ACTION_INVOKED, d, - _ev_action_invoked_free, NULL)) goto cleanup; - - return; - -cleanup: - free(d->action_key); - free(d); -} - -static void -_release(void) -{ - if (_elm_sysnotif_proxy) - { - eldbus_proxy_unref(_elm_sysnotif_proxy); - _elm_sysnotif_proxy = NULL; - } - - if (_elm_sysnotif_obj) - { - eldbus_object_unref(_elm_sysnotif_obj); - _elm_sysnotif_obj = NULL; - } -} - -static void -_update(void) -{ - _release(); - _elm_sysnotif_obj = eldbus_object_get(_elm_sysnotif_conn, BUS, OBJ); - _elm_sysnotif_proxy = eldbus_proxy_get(_elm_sysnotif_obj, INTERFACE); - _elm_sys_notify_capabilities_get(); - - eldbus_proxy_signal_handler_add(_elm_sysnotif_proxy, "NotificationClosed", - _on_notification_closed, NULL); - - eldbus_proxy_signal_handler_add(_elm_sysnotif_proxy, "ActionInvoked", - _on_action_invoked, NULL); -} - -static void -_name_owner_get_cb(void *data EINA_UNUSED, - const Eldbus_Message *msg, - Eldbus_Pending *pending EINA_UNUSED) -{ - const char *errname, *errmsg; - - if (eldbus_message_error_get(msg, &errname, &errmsg)) - ERR("Eldbus Error: %s %s", errname, errmsg); - else - _update(); -} - -static void -_name_owner_changed_cb(void *data EINA_UNUSED, - const char *bus EINA_UNUSED, - const char *old_id EINA_UNUSED, - const char *new_id) -{ - if ((!new_id) || (*new_id == '\0')) - _release(); - else - _update(); + eo_do(_singleton, + elm_obj_sys_notify_servers_set(ELM_SYS_NOTIFY_SERVER_NONE)); + eo_del(_singleton); } EAPI Eina_Bool elm_need_sys_notify(void) { - if (_elm_need_sys_notify) return EINA_TRUE; + Elm_Sys_Notify_Server servers = ELM_SYS_NOTIFY_SERVER_NONE; + Elm_Sys_Notify *manager; + Srv i; - if (!elm_need_eldbus()) return EINA_FALSE; + /* In theory, there can be N notification managers, but + * in the implementation there will be only one: the + * singleton which is initialized here. */ + manager = elm_sys_notify_singleton_get(); + if (EINA_UNLIKELY(!manager)) + { + CRI("Failed to get notification manager"); + return EINA_FALSE; + } - if (!ELM_EVENT_SYS_NOTIFY_NOTIFICATION_CLOSED) - ELM_EVENT_SYS_NOTIFY_NOTIFICATION_CLOSED = ecore_event_type_new(); + /* If servers have alread been configured, this is not the right + * function to be called! */ + if (elm_sys_notify_servers_get(manager) != ELM_SYS_NOTIFY_SERVER_NONE) + { + ERR("Notification servers have already been configured. " + "Use elm_sys_notify_servers_set() instead."); + return EINA_FALSE; + } - if (!ELM_EVENT_SYS_NOTIFY_ACTION_INVOKED) - ELM_EVENT_SYS_NOTIFY_ACTION_INVOKED = ecore_event_type_new(); + /* Register available notification servers */ + for (i = SRV_DBUS; i < __SRV_LAST; ++i) + if (_class_getters[i]) + servers |= (1 << i); - _elm_sysnotif_conn = eldbus_connection_get(ELDBUS_CONNECTION_TYPE_SESSION); + /* If no server are available, don't even bother... */ + if (servers == ELM_SYS_NOTIFY_SERVER_NONE) + return EINA_FALSE; - eldbus_name_owner_changed_callback_add(_elm_sysnotif_conn, BUS, - _name_owner_changed_cb, NULL, - EINA_FALSE); - - eldbus_name_owner_get(_elm_sysnotif_conn, BUS, _name_owner_get_cb, NULL); - - _elm_need_sys_notify = EINA_TRUE; - - return EINA_TRUE; + return elm_sys_notify_servers_set(manager, servers); } -void -_elm_unneed_sys_notify(void) +EAPI void +elm_sys_notify_send(unsigned int replaces_id, + const char *icon, + const char *summary, + const char *body, + Elm_Sys_Notify_Urgency urgency, + int timeout, + Elm_Sys_Notify_Send_Cb cb, + const void *cb_data) { - if (!_elm_need_sys_notify) return; - - _elm_need_sys_notify = EINA_FALSE; - - _release(); - - eldbus_connection_unref(_elm_sysnotif_conn); - _elm_sysnotif_conn = NULL; + eo_do(_singleton, + elm_obj_sys_notify_interface_send(replaces_id, + icon, summary, body, + urgency, timeout, + cb, cb_data)); } + +EAPI void +elm_sys_notify_close(unsigned int id) +{ + eo_do(_singleton, + elm_obj_sys_notify_interface_close(id)); +} + +#include "elm_sys_notify.eo.c" + diff --git a/legacy/elementary/src/lib/elm_sys_notify.eo b/legacy/elementary/src/lib/elm_sys_notify.eo new file mode 100644 index 0000000000..7eb0df2dfb --- /dev/null +++ b/legacy/elementary/src/lib/elm_sys_notify.eo @@ -0,0 +1,90 @@ +enum Elm.Sys_Notify.Server +{ + none = 0, [[No notificationserver (disables notifications)]] + dbus = 1 << 0 [[Use DBus as a notification server]] +} + +enum Elm.Sys_Notify.Closed_Reason +{ + [[The reason the notification was closed + + @since 1.8 + ]] + + legacy: elm_sys_notify_closed; + + expired, [[The notification expired]] + dismissed, [[The notification was dismissed by the user]] + requested, [[The notification was closed by a call to CloseNotification method]] + undefined [[Undefined/reserved reasons]] +} + +struct Elm.Sys_Notify.Notification_Closed +{ + [[Data on event when notification closed is emitted + + @since 1.8 + ]] + id: uint; [[ID of the notification]] + reason: Elm.Sys_Notify.Closed_Reason; [[The reason the notification was closed]] +} + +struct Elm.Sys_Notify.Action_Invoked +{ + [[Data on event when the action invoked is emitted + + @since 1.8 + ]] + id: uint; [[ID of the notification]] + action_key: char *; [[The key of the action invoked. These match the + keys sent over in the list of actions]] +} + +class Elm.Sys_Notify (Eo.Base, Elm.Sys_Notify_Interface) +{ + legacy_prefix: elm_sys_notify; + eo_prefix: elm_obj_sys_notify; + + methods { + @property servers { + get { + [[Get the notification servers that have been registered + + @since 1.16 + ]] + } + set { + [[Set the notifications server to be used. + + Note: This is an advanced function that should be used only to + fullfill very specific purposes. Use elm_need_sys_notify() + which activates the default available notification + servers. + ]] + return: bool; [[$true on success, $false on failure]] + } + values { + servers: Elm.Sys_Notify.Server; [[Binary mask of servers to enable. + If a server is not present in the binary mask but was previously + registered, it will be unregistered.]] + } + } + + singleton_get @class { + [[Returns the singleton instance of the notification manager + Elm.Sys_Notify. It is initialized upon the first call of this + function]] + return: Elm.Sys_Notify *; [[The unique notification manager]] + } + } + + implements { + class.constructor; + Eo.Base.constructor; + Eo.Base.destructor; + Elm.Sys_Notify_Interface.send; + Elm.Sys_Notify_Interface.simple_send; + Elm.Sys_Notify_Interface.close; + } +} + diff --git a/legacy/elementary/src/lib/elm_sys_notify.h b/legacy/elementary/src/lib/elm_sys_notify.h index aeff5a006d..4534838ca7 100644 --- a/legacy/elementary/src/lib/elm_sys_notify.h +++ b/legacy/elementary/src/lib/elm_sys_notify.h @@ -1,67 +1,25 @@ #ifndef ELM_SYS_NOTIFY_H #define ELM_SYS_NOTIFY_H -/** - * The reason the notification was closed - * - * @since 1.8 - */ -typedef enum _Elm_Sys_Notify_Closed_Reason -{ - ELM_SYS_NOTIFY_CLOSED_EXPIRED, /** The notification expired. */ - ELM_SYS_NOTIFY_CLOSED_DISMISSED, /** The notification was dismissed by the user. */ - ELM_SYS_NOTIFY_CLOSED_REQUESTED, /** The notification was closed by a call to CloseNotification method. */ - ELM_SYS_NOTIFY_CLOSED_UNDEFINED /** Undefined/reserved reasons. */ -} Elm_Sys_Notify_Closed_Reason; - -/** - * Urgency levels of a notification - * - * @see elm_sys_notify_send() - * - * @since 1.8 - */ -typedef enum _Elm_Sys_Notify_Urgency -{ - ELM_SYS_NOTIFY_URGENCY_LOW, - ELM_SYS_NOTIFY_URGENCY_NORMAL, - ELM_SYS_NOTIFY_URGENCY_CRITICAL -} Elm_Sys_Notify_Urgency; - typedef void (*Elm_Sys_Notify_Send_Cb)(void *data, unsigned int id); +#include "elm_sys_notify_interface.eo.h" +#include "elm_sys_notify_interface.eo.legacy.h" +#include "elm_sys_notify.eo.h" +#include "elm_sys_notify.eo.legacy.h" + /** * Emitted when the signal NotificationClosed is received. + * @since 1.8 */ EAPI extern int ELM_EVENT_SYS_NOTIFY_NOTIFICATION_CLOSED; /** * Emitted when the signal ActionInvoked is received. + * @since 1.8 */ EAPI extern int ELM_EVENT_SYS_NOTIFY_ACTION_INVOKED; /**< A Action has been invoked. */ -/** - * Data on event when Notification Closed is emitted. - * - * @since 1.8 - */ -typedef struct _Elm_Sys_Notify_Notification_Closed -{ - unsigned int id; /**< ID of the notification. */ - Elm_Sys_Notify_Closed_Reason reason; /**< The Reason the notification was closed. */ -} Elm_Sys_Notify_Notification_Closed; - - -/** - * Data on event when Action Invoked is emitted. - * - * @since 1.8 - */ -typedef struct _Elm_Sys_Notify_Action_Invoked -{ - unsigned int id; /**< ID of the notification. */ - char *action_key; /**< The key of the action invoked. These match the keys sent over in the list of actions. */ -} Elm_Sys_Notify_Action_Invoked; /** * @def elm_sys_notify_simple_send @@ -82,22 +40,9 @@ typedef struct _Elm_Sys_Notify_Action_Invoked -1, NULL, NULL) /** - * Causes a notification to be forcefully closed and removed from the user's - * view. It can be used, for example, in the event that what the notification - * pertains to is no longer relevant, or to cancel a notification * with no - * expiration time. - * - * @param id Notification id - * - * @note If the notification no longer exists, - * an empty D-BUS Error message is sent back. - * - * @since 1.8 - */ -EAPI void elm_sys_notify_close(unsigned int id); - -/** - * Sends a notification to the notification server. + * Sends a notification to the notification servers that have + * been registered by elm_need_sys_notify() or + * elm_sys_notify_servers_set(). * * @param replaces_id Notification ID that this notification replaces. * The value 0 means a new notification. @@ -120,4 +65,21 @@ EAPI void elm_sys_notify_send(unsigned int replaces_id, int timeout, Elm_Sys_Notify_Send_Cb cb, const void *cb_data); + +/** + * Causes a notification to be forcefully closed and removed from the user's + * view. It can be used, for example, in the event that what the notification + * pertains to is no longer relevant, or to cancel a notification * with no + * expiration time. + * + * @param id Notification id + * + * @note If the notification no longer exists, + * an empty D-BUS Error message is sent back. + * + * @since 1.8 + */ +EAPI void elm_sys_notify_close(unsigned int id); + #endif + diff --git a/legacy/elementary/src/lib/elm_sys_notify_dbus.c b/legacy/elementary/src/lib/elm_sys_notify_dbus.c new file mode 100644 index 0000000000..bd0fa1e059 --- /dev/null +++ b/legacy/elementary/src/lib/elm_sys_notify_dbus.c @@ -0,0 +1,398 @@ +#ifdef HAVE_CONFIG_H +# include "elementary_config.h" +#endif + +#include + +#include "elm_priv.h" + +#include "elm_sys_notify_dbus.eo.h" +#include "elm_sys_notify_dbus.eo.legacy.h" + +#define MY_CLASS ELM_SYS_NOTIFY_DBUS_CLASS + +#define OBJ "/org/freedesktop/Notifications" +#define BUS "org.freedesktop.Notifications" +#define INTERFACE "org.freedesktop.Notifications" + +static Eldbus_Connection *_elm_sysnotif_conn = NULL; +static Eldbus_Object *_elm_sysnotif_obj = NULL; +static Eldbus_Proxy *_elm_sysnotif_proxy = NULL; + +static Eina_Bool _has_markup = EINA_FALSE; + +typedef struct _Elm_Sys_Notify_Send_Data +{ + Elm_Sys_Notify_Send_Cb cb; + const void *data; +} Elm_Sys_Notify_Send_Data; + +static void +_elm_sys_notify_marshal_dict_byte(Eldbus_Message_Iter *array, + const char *key, + const char value) +{ + Eldbus_Message_Iter *var, *entry; + + eldbus_message_iter_arguments_append(array, "{sv}", &entry); + eldbus_message_iter_basic_append(entry, 's', key); + + var = eldbus_message_iter_container_new(entry, 'v', "y"); + eldbus_message_iter_basic_append(var, 'y', value); + eldbus_message_iter_container_close(entry, var); + eldbus_message_iter_container_close(array, entry); +} + +static void +_elm_sys_notify_marshal_dict_string(Eldbus_Message_Iter *array, + const char *key, + const char *value) +{ + Eldbus_Message_Iter *var, *entry; + + eldbus_message_iter_arguments_append(array, "{sv}", &entry); + eldbus_message_iter_basic_append(entry, 's', key); + + var = eldbus_message_iter_container_new(entry, 'v', "s"); + eldbus_message_iter_basic_append(var, 's', value); + eldbus_message_iter_container_close(entry, var); + eldbus_message_iter_container_close(array, entry); +} + +static void +_get_capabilities_cb(void *data EINA_UNUSED, + const Eldbus_Message *msg, + Eldbus_Pending *pending EINA_UNUSED) +{ + char *val; + Eldbus_Message_Iter *arr; + + if (eldbus_message_error_get(msg, NULL, NULL) || + !eldbus_message_arguments_get(msg, "as", &arr)) goto end; + + while (eldbus_message_iter_get_and_next(arr, 's', &val)) + if (!strcmp(val, "body-markup")) + { + _has_markup = EINA_TRUE; + return; + } + +end: + _has_markup = EINA_FALSE; +} + +void +_elm_sys_notify_capabilities_get(void) +{ + EINA_SAFETY_ON_NULL_RETURN(_elm_sysnotif_proxy); + + if (!eldbus_proxy_call(_elm_sysnotif_proxy, "GetCapabilities", + _get_capabilities_cb, NULL, -1, "")) + ERR("Error sending message: "INTERFACE".GetCapabilities."); +} + +static void +_close_notification_cb(void *data EINA_UNUSED, + const Eldbus_Message *msg, + Eldbus_Pending *pending EINA_UNUSED) +{ + const char *errname, *errmsg; + + if (eldbus_message_error_get(msg, &errname, &errmsg)) + { + if (errmsg && errmsg[0] == '\0') + INF("Notification no longer exists."); + else + ERR("Eldbus Error: %s %s", errname, errmsg); + } +} + +EOLIAN static void +_elm_sys_notify_dbus_elm_sys_notify_interface_close(const Eo *obj EINA_UNUSED, + void *sd EINA_UNUSED, + unsigned int id) +{ + EINA_SAFETY_ON_NULL_RETURN(_elm_sysnotif_proxy); + + if (!eldbus_proxy_call(_elm_sysnotif_proxy, "CloseNotification", + _close_notification_cb, NULL, -1, "u", id)) + ERR("Error sending message: "INTERFACE".CloseNotification."); +} + +static void +_notify_cb(void *data, + const Eldbus_Message *msg, + Eldbus_Pending *pending EINA_UNUSED) +{ + const char *errname, *errmsg; + Elm_Sys_Notify_Send_Data *d = data; + unsigned int id = 0; + + if (eldbus_message_error_get(msg, &errname, &errmsg)) + ERR("Error: %s %s", errname, errmsg); + else if (!eldbus_message_arguments_get(msg, "u", &id)) + { + ERR("Error getting return values of "INTERFACE".Notify."); + id = 0; + } + + if (d->cb) d->cb((void *)d->data, id); + free(d); +} + +EOLIAN static void +_elm_sys_notify_dbus_elm_sys_notify_interface_send(const Eo *obj EINA_UNUSED, + void *sd EINA_UNUSED, + unsigned int replaces_id, + const char *icon, + const char *summary, + const char *body, + Elm_Sys_Notify_Urgency urgency, + int timeout, + Elm_Sys_Notify_Send_Cb cb, + const void *cb_data) +{ + Eldbus_Message *msg; + Eldbus_Message_Iter *iter, *actions, *hints; + Elm_Sys_Notify_Send_Data *data; + char *body_free = NULL; + char *desk_free = NULL; + const char *deskentry = elm_app_desktop_entry_get(); + const char *appname = elm_app_name_get(); + + EINA_SAFETY_ON_NULL_RETURN(_elm_sysnotif_proxy); + + data = malloc(sizeof(Elm_Sys_Notify_Send_Data)); + EINA_SAFETY_ON_NULL_GOTO(data, error); + data->cb = cb; + data->data = cb_data; + + if (!icon) icon = ""; + if (!summary) summary = ""; + if (!body) + body = ""; + else if (!_has_markup) + body = body_free = elm_entry_markup_to_utf8(body); + + msg = eldbus_proxy_method_call_new(_elm_sysnotif_proxy, "Notify"); + + iter = eldbus_message_iter_get(msg); + eldbus_message_iter_arguments_append(iter, "susssas", appname, replaces_id, + icon, summary, body, &actions); + /* actions */ + eldbus_message_iter_container_close(iter, actions); + + /* hints */ + eldbus_message_iter_arguments_append(iter, "a{sv}", &hints); + _elm_sys_notify_marshal_dict_byte(hints, "urgency", (char) urgency); + + if (strcmp(deskentry, "")) + { + deskentry = ecore_file_file_get(deskentry); + deskentry = desk_free = ecore_file_strip_ext(deskentry); + _elm_sys_notify_marshal_dict_string(hints, "desktop_entry", deskentry); + } + eldbus_message_iter_container_close(iter, hints); + + /* timeout */ + eldbus_message_iter_arguments_append(iter, "i", timeout); + + eldbus_proxy_send(_elm_sysnotif_proxy, msg, _notify_cb, data, -1); + free(desk_free); + free(body_free); + return; + +error: + if (cb) cb((void *)cb_data, 0); +} + +EOLIAN static void +_elm_sys_notify_dbus_elm_sys_notify_interface_simple_send(const Eo *obj, + void *sd, + const char *icon, + const char *summary, + const char *body) +{ + _elm_sys_notify_dbus_elm_sys_notify_interface_send(obj, sd, + 0, icon, summary, body, + ELM_SYS_NOTIFY_URGENCY_NORMAL, + -1, NULL, NULL); +} + +static void +_on_notification_closed(void *data EINA_UNUSED, + const Eldbus_Message *msg) +{ + const char *errname; + const char *errmsg; + Elm_Sys_Notify_Notification_Closed *d; + + if (eldbus_message_error_get(msg, &errname, &errmsg)) + { + ERR("Eldbus Error: %s %s", errname, errmsg); + return; + } + + d = malloc(sizeof(*d)); + + if (!eldbus_message_arguments_get(msg, "uu", &(d->id), &(d->reason))) + { + ERR("Error processing signal: "INTERFACE".NotificationClosed."); + goto cleanup; + } + + if (!ecore_event_add(ELM_EVENT_SYS_NOTIFY_NOTIFICATION_CLOSED, d, + NULL, NULL)) goto cleanup; + + return; + +cleanup: + free(d); +} + +static void +_ev_action_invoked_free(void *data EINA_UNUSED, + void *ev_data) +{ + Elm_Sys_Notify_Action_Invoked *d = ev_data; + + free(d->action_key); + free(d); +} + +static void +_on_action_invoked(void *data EINA_UNUSED, + const Eldbus_Message *msg) +{ + const char *errname; + const char *aux; + + Elm_Sys_Notify_Action_Invoked *d; + + if (eldbus_message_error_get(msg, &errname, &aux)) + { + ERR("Eldbus Error: %s %s", errname, aux); + return; + } + + d = calloc(1, sizeof(*d)); + if (!d) + { + ERR("Fail to allocate memory"); + return; + } + + if (!eldbus_message_arguments_get(msg, "us", &(d->id), &aux)) + { + ERR("Error processing signal: "INTERFACE".ActionInvoked."); + goto cleanup; + } + + d->action_key = strdup(aux); + + if (!ecore_event_add(ELM_EVENT_SYS_NOTIFY_ACTION_INVOKED, d, + _ev_action_invoked_free, NULL)) goto cleanup; + + return; + +cleanup: + free(d->action_key); + free(d); +} + +static void +_release(void) +{ + if (_elm_sysnotif_proxy) + { + eldbus_proxy_unref(_elm_sysnotif_proxy); + _elm_sysnotif_proxy = NULL; + } + + if (_elm_sysnotif_obj) + { + eldbus_object_unref(_elm_sysnotif_obj); + _elm_sysnotif_obj = NULL; + } +} + +static void +_update(void) +{ + _release(); + _elm_sysnotif_obj = eldbus_object_get(_elm_sysnotif_conn, BUS, OBJ); + _elm_sysnotif_proxy = eldbus_proxy_get(_elm_sysnotif_obj, INTERFACE); + _elm_sys_notify_capabilities_get(); + + eldbus_proxy_signal_handler_add(_elm_sysnotif_proxy, "NotificationClosed", + _on_notification_closed, NULL); + + eldbus_proxy_signal_handler_add(_elm_sysnotif_proxy, "ActionInvoked", + _on_action_invoked, NULL); +} + +static void +_name_owner_get_cb(void *data EINA_UNUSED, + const Eldbus_Message *msg, + Eldbus_Pending *pending EINA_UNUSED) +{ + const char *errname, *errmsg; + + if (eldbus_message_error_get(msg, &errname, &errmsg)) + ERR("Eldbus Error: %s %s", errname, errmsg); + else + _update(); +} + +static void +_name_owner_changed_cb(void *data EINA_UNUSED, + const char *bus EINA_UNUSED, + const char *old_id EINA_UNUSED, + const char *new_id) +{ + if ((!new_id) || (*new_id == '\0')) + _release(); + else + _update(); +} + +EOLIAN static Eo_Base * +_elm_sys_notify_dbus_eo_base_constructor(Eo *obj, + void *sd EINA_UNUSED) +{ + /* Don't create the same object twice (singleton) */ + if (!_elm_sysnotif_conn) + { + if (!elm_need_eldbus()) return NULL; + + _elm_sysnotif_conn = eldbus_connection_get(ELDBUS_CONNECTION_TYPE_SESSION); + if (!_elm_sysnotif_conn) return NULL; + + eldbus_name_owner_changed_callback_add(_elm_sysnotif_conn, BUS, + _name_owner_changed_cb, NULL, + EINA_FALSE); + + eldbus_name_owner_get(_elm_sysnotif_conn, BUS, _name_owner_get_cb, NULL); + + obj = eo_do_super_ret(obj, MY_CLASS, obj, eo_constructor()); + return obj; + } + + ERR("Elm.Sys_Notify.Dbus is a singleton. It has already been created"); + return NULL; +} + +EOLIAN static void +_elm_sys_notify_dbus_eo_base_destructor(Eo *obj, + void *sd EINA_UNUSED) +{ + _release(); + + eldbus_connection_unref(_elm_sysnotif_conn); + _elm_sysnotif_conn = NULL; + eo_do_super(obj, MY_CLASS, eo_destructor()); +} + + +#include "elm_sys_notify_dbus.eo.c" + diff --git a/legacy/elementary/src/lib/elm_sys_notify_dbus.eo b/legacy/elementary/src/lib/elm_sys_notify_dbus.eo new file mode 100644 index 0000000000..df8eb9c855 --- /dev/null +++ b/legacy/elementary/src/lib/elm_sys_notify_dbus.eo @@ -0,0 +1,14 @@ +class Elm.Sys_Notify.Dbus (Eo.Base, Elm.Sys_Notify_Interface) +{ + eo_prefix: elm_obj_sys_notify_dbus; + data: null; + + implements { + Eo.Base.constructor; + Eo.Base.destructor; + Elm.Sys_Notify_Interface.send; + Elm.Sys_Notify_Interface.simple_send; + Elm.Sys_Notify_Interface.close; + } +} + diff --git a/legacy/elementary/src/lib/elm_sys_notify_interface.c b/legacy/elementary/src/lib/elm_sys_notify_interface.c new file mode 100644 index 0000000000..c630afec63 --- /dev/null +++ b/legacy/elementary/src/lib/elm_sys_notify_interface.c @@ -0,0 +1,9 @@ +#ifdef HAVE_CONFIG_H +# include "elementary_config.h" +#endif + +#include + +#include "elm_priv.h" +#include "elm_sys_notify_interface.eo.c" + diff --git a/legacy/elementary/src/lib/elm_sys_notify_interface.eo b/legacy/elementary/src/lib/elm_sys_notify_interface.eo new file mode 100644 index 0000000000..627586b09c --- /dev/null +++ b/legacy/elementary/src/lib/elm_sys_notify_interface.eo @@ -0,0 +1,66 @@ +enum Elm.Sys_Notify.Urgency +{ + [[Urgency levels of a notification + + @since 1.8 + ]] + low, [[Low urgency]] + normal, [[Normal urgency]] + critical [[Critical urgency]] +} + +interface Elm.Sys_Notify_Interface +{ + eo_prefix: elm_obj_sys_notify_interface; + legacy_prefix: elm_sys_notify_interface; + + methods { + send @const { + [[Causes a notification to be forcefully closed and removed from the + user's view. It can be used, for example, in the event that what the + notification pertains to is no longer relevant, or to cancel a + notification with no expiration time. + + @since 1.8 + ]] + params { + @in replaces_id: uint; [[Notification ID that this notification replaces. + The value 0 means a new notification.]] + @in icon: const(char) *; [[The optional program icon of the calling application]] + @in summary: const(char) *; [[The summary text briefly describing the notification]] + @in body: const(char) * @optional; [[The optional detailed body text. Can be empty]] + @in urgency: Elm.Sys_Notify.Urgency; [[The urgency level]] + @in timeout: int; [[Timeout display in milliseconds]] + @in cb: Elm_Sys_Notify_Send_Cb; [[Callback used to retrieve the notification id + returned by the Notification Server]] + @in cb_data: const(void) * @optional; [[Optional context data]] + } + } + simple_send @const { + [[Create a new notification just with Icon, Body and Summary. + It is a helper that wraps the send method + + @since 1.16 + ]] + + params { + @in icon: const(char) *; [[The optional program icon of the calling application]] + @in summary: const(char) *; [[The summary text briefly describing the notification]] + @in body: const(char) *; [[The optional detailed body text. Can be empty]] + } + } + close @const { + [[Causes a notification to be forcefully closed and removed from the + user's view. It can be used, for example, in the event that what the + notification pertains to is no longer relevant, or to cancel a + notification with no expiration time. + + @since 1.8 + ]] + params { + @in id: uint; [[Notification ID]] + } + } + } +} +