sys_notify: support several notification servers

Summary:
An Eo class Elm.Sys_Notify acts as a manager of notification
servers. A manager registers and unregisters notification servers.

Notification servers implement the Elm.Sys_Notify_Interface
which allows to send and close notifications.

Currently, only the DBus server is implemented (legacy code).

Even though there are many changes in the code, there should
be no API nor ABI breaks.

Reviewers: naguirre, seoz, cedric

Reviewed By: cedric

Differential Revision: https://phab.enlightenment.org/D3172

Signed-off-by: Cedric BAIL <cedric@osg.samsung.com>
This commit is contained in:
Jean Guyomarc'h 2016-01-04 11:49:11 -08:00 committed by Cedric BAIL
parent 2f5bed1886
commit 42cb79017d
8 changed files with 862 additions and 397 deletions

View File

@ -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 \

View File

@ -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"

View File

@ -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;
}
}

View File

@ -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

View File

@ -0,0 +1,398 @@
#ifdef HAVE_CONFIG_H
# include "elementary_config.h"
#endif
#include <Elementary.h>
#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"

View File

@ -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;
}
}

View File

@ -0,0 +1,9 @@
#ifdef HAVE_CONFIG_H
# include "elementary_config.h"
#endif
#include <Elementary.h>
#include "elm_priv.h"
#include "elm_sys_notify_interface.eo.c"

View File

@ -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]]
}
}
}
}