This is an automated email from the git hooks/post-receive script.
git pushed a commit to branch master
in repository enlightenment.
View the commit online.
commit b23eedae98c180a8a2d7e4e33643d0dedb16c903
Author: Carsten Haitzler <ras...@rasterman.com>
AuthorDate: Sat Jun 18 00:44:17 2022 +0100
notifications - extend to support actions, links and img tags
we didn't support enough of noktifications to make everyone happy -
this is why ffox, chrome etc. did their own notification windows and
didn't use e's notifications. we now advertise doing everything. we
say w edo sound though don't.... will add that later, but this now
means we really do a lot more and thus pushes these other
notifications into e's notifications so we're much better now and this
annoyance i have noticed is now gone.
@feat
---
src/bin/e_notification.c | 535 +++++++++++++++++++++++----------
src/bin/e_notification.h | 43 ++-
src/modules/notification/e_mod_main.c | 17 +-
src/modules/notification/e_mod_main.h | 51 ++--
src/modules/notification/e_mod_popup.c | 175 ++++++++++-
5 files changed, 621 insertions(+), 200 deletions(-)
diff --git a/src/bin/e_notification.c b/src/bin/e_notification.c
index b185f395b..8ddac7c0f 100644
--- a/src/bin/e_notification.c
+++ b/src/bin/e_notification.c
@@ -2,8 +2,8 @@
typedef struct _Notification_Data
{
- Eldbus_Connection *conn;
- Eldbus_Service_Interface *iface;
+ Eldbus_Connection *conn;
+ Eldbus_Service_Interface *iface;
E_Notification_Notify_Cb notify_cb;
E_Notification_Close_Cb close_cb;
void *data;
@@ -15,15 +15,28 @@ static Notification_Data *n_data = NULL;
static void
_notification_free(E_Notification_Notify *notify)
{
+ int i;
+
EINA_SAFETY_ON_NULL_RETURN(notify);
- eina_stringshare_del(notify->app_name);
- eina_stringshare_del(notify->body);
- eina_stringshare_del(notify->icon.icon);
- if (notify->icon.icon_path)
- eina_stringshare_del(notify->icon.icon_path);
- eina_stringshare_del(notify->summary);
- if (notify->icon.raw.data)
- free(notify->icon.raw.data);
+ if (notify->app_name) eina_stringshare_del(notify->app_name);
+ if (notify->body) eina_stringshare_del(notify->body);
+ if (notify->icon.icon) eina_stringshare_del(notify->icon.icon);
+ if (notify->icon.icon_path) eina_stringshare_del(notify->icon.icon_path);
+ if (notify->summary) eina_stringshare_del(notify->summary);
+ if (notify->icon.raw.data) free(notify->icon.raw.data);
+ if (notify->category) eina_stringshare_del(notify->category);
+ if (notify->desktop_entry) eina_stringshare_del(notify->desktop_entry);
+ if (notify->sound_file) eina_stringshare_del(notify->sound_file);
+ if (notify->sound_name) eina_stringshare_del(notify->sound_name);
+ if (notify->actions)
+ {
+ for (i = 0; notify->actions[i].action; i++)
+ {
+ eina_stringshare_del(notify->actions[i].action);
+ eina_stringshare_del(notify->actions[i].label);
+ }
+ free(notify->actions);
+ }
free(notify);
}
@@ -31,12 +44,15 @@ static void
hints_dict_iter(void *data, const void *key, Eldbus_Message_Iter *var)
{
E_Notification_Notify *n = data;
- if (!strcmp(key, "image-data") || !strcmp(key, "image_data"))
+
+ if ((!strcmp(key, "image-data")) || (!strcmp(key, "image_data")) ||
+ (!strcmp(key, "icon_data")))
{
Eldbus_Message_Iter *st, *data_iter;
int w, h, r, bits, channels;
Eina_Bool alpha;
unsigned char *raw_data;
+
if (!eldbus_message_iter_arguments_get(var, "(iiibiiay)", &st))
return;
if (!eldbus_message_iter_arguments_get(st, "iiibiiay", &w, &h, &r,
@@ -54,21 +70,135 @@ hints_dict_iter(void *data, const void *key, Eldbus_Message_Iter *var)
n->icon.raw.data = "" * n->icon.raw.data_size);
EINA_SAFETY_ON_NULL_RETURN(n->icon.raw.data);
memcpy(n->icon.raw.data, raw_data, sizeof(char) * n->icon.raw.data_size);
+ printf("NOT: image-data="" w, h, alpha);
+ }
+ else if (!strcmp(key, "image-path") || !strcmp(key, "image_path"))
+ {
+ eldbus_message_iter_arguments_get(var, "s", &n->icon.icon_path);
+ n->icon.icon_path = eina_stringshare_add(n->icon.icon_path);
+ printf("NOT: image-path=[%s]\n", n->icon.icon_path);
+ // path to image file
}
else if (!strcmp(key, "urgency"))
{
unsigned char urgency;
+
eldbus_message_iter_arguments_get(var, "y", &urgency);
- if (urgency < 3)
- n->urgency = urgency;
+ if (urgency < 3) n->urgency = urgency;
+ printf("NOT: urgency=%i\n", n->urgency);
+ // 0=low, 1=normal, 2=critical
}
- else if (!strcmp(key, "image-path") || !strcmp(key, "image_path"))
+ else if (!strcmp(key, "category"))
+ { // XXX: store category
+ const char *val = NULL;
+
+ eldbus_message_iter_arguments_get(var, "s", &val);
+ printf("NOT: category=[%s]\n", val);
+ // "device" A generic device-related notification that doesn't fit into any other category.
+ // "device.added" A device, such as a USB device, was added to the system.
+ // "device.error" A device had some kind of error.
+ // "device.removed" A device, such as a USB device, was removed from the system.
+ // "email" A generic e-mail-related notification that doesn't fit into any other category.
+ // "email.arrived" A new e-mail notification.
+ // "email.bounced" A notification stating that an e-mail has bounced.
+ // "im" A generic instant message-related notification that doesn't fit into any other category.
+ // "im.error" An instant message error notification.
+ // "im.received" A received instant message notification.
+ // "network" A generic network notification that doesn't fit into any other category.
+ // "network.connected" A network connection notification, such as successful sign-on to a network service. This should not be confused with device.added for new network devices.
+ // "network.disconnected" A network disconnected notification. This should not be confused with device.removed for disconnected network devices.
+ // "network.error" A network-related or connection-related error.
+ // "presence" A generic presence change notification that doesn't fit into any other category, such as going away or idle.
+ // "presence.offline" An offline presence change notification.
+ // "presence.online" An online presence change notification.
+ // "transfer" A generic file transfer or download notification that doesn't fit into any other category.
+ // "transfer.complete" A file transfer or download complete notification.
+ // "transfer.error" A file transfer or download error.
+ if (val) n->category = eina_stringshare_add(val);
+ }
+ else if (!strcmp(key, "desktop-entry"))
{
- eldbus_message_iter_arguments_get(var, "s", &n->icon.icon_path);
- n->icon.icon_path = eina_stringshare_add(n->icon.icon_path);
+ const char *val = NULL;
+
+ eldbus_message_iter_arguments_get(var, "s", &val);
+ printf("NOT: desktop-entry=[%s]\n", val);
+ // if rage.desktop -> "rage"
+ // if terminology.desktop -> "terminology"
+ if (val) n->desktop_entry = eina_stringshare_add(val);
}
-}
+ else if (!strcmp(key, "icon-actions"))
+ {
+ Eina_Bool val = 0;
+
+ eldbus_message_iter_arguments_get(var, "b", &val);
+ printf("NOT: icon-actions=%i\n", val);
+ // 1 == interpret action identifier == named icon in icon naming standards
+ n->icon_actions = val;
+ }
+ else if (!strcmp(key, "resident"))
+ {
+ Eina_Bool val = 0;
+
+ eldbus_message_iter_arguments_get(var, "b", &val);
+ printf("NOT: resident=%i\n", val);
+ // 1== remove notification when action invoked - no timeout
+ n->resident = val;
+ }
+ else if (!strcmp(key, "supress-sound"))
+ {
+ Eina_Bool val = 0;
+ eldbus_message_iter_arguments_get(var, "b", &val);
+ printf("NOT: supress-sound=%i\n", val);
+ // 1== remove notification when action invoked - no timeout
+ n->suppress_sound = val;
+ }
+ else if (!strcmp(key, "sound-file"))
+ {
+ const char *val = NULL;
+
+ eldbus_message_iter_arguments_get(var, "s", &val);
+ printf("NOT: sound-file=[%s]\n", val);
+ // path to sound file to play
+ if (val) n->sound_file = eina_stringshare_add(val);
+ }
+ else if (!strcmp(key, "sound-name"))
+ {
+ const char *val = NULL;
+
+ eldbus_message_iter_arguments_get(var, "s", &val);
+ printf("NOT: sound-file=[%s]\n", val);
+ // sound naming spec to play
+ // http://0pointer.de/public/sound-naming-spec.html
+ if (val) n->sound_name = eina_stringshare_add(val);
+ }
+ else if (!strcmp(key, "transient"))
+ {
+ Eina_Bool val = 0;
+
+ eldbus_message_iter_arguments_get(var, "b", &val);
+ printf("NOT: transient=%i\n", val);
+ n->transient = val;
+ }
+ else if (!strcmp(key, "x"))
+ {
+ int val = 0;
+
+ eldbus_message_iter_arguments_get(var, "i", &val);
+ printf("NOT: x=%i\n", val);
+ n->x = val;
+ n->have_xy = EINA_TRUE;
+ }
+ else if (!strcmp(key, "y"))
+ {
+ int val = 0;
+
+ eldbus_message_iter_arguments_get(var, "i", &val);
+ printf("NOT: y=%i\n", val);
+ n->y = val;
+ n->have_xy = EINA_TRUE;
+ }
+}
/* this function should be external in edje for use in cases such as this module.
*
@@ -93,134 +223,188 @@ _text_escape(Eina_Strbuf *txt, const char *text)
return advance;
}
-/* hardcoded list of allowed tags based on
- * https://people.gnome.org/~mccann/docs/notification-spec/notification-spec-latest.html#markup
- */
-static const char *tags[] =
+static int
+_tag_len(const char *txt)
{
- "<b",
- "<i",
- "<u",
- //"<a", FIXME: we can't actually display these right now
- //"<img",
-};
+ const char *s;
+ Eina_Bool backslash = EINA_FALSE;
+ Eina_Bool inquote = EINA_FALSE, indblquote = EINA_FALSE;
-static const char *
-_get_tag(const char *c)
+ if (txt[0] != '<') return 0;
+ for (s = txt; *s; s++)
+ {
+ if (!backslash)
+ {
+ if (*s == '\\') backslash = EINA_TRUE;
+ else
+ {
+ if (inquote)
+ {
+ if (*s == '\'') inquote = EINA_FALSE;
+ }
+ else if (indblquote)
+ {
+ if (*s == '"') indblquote = EINA_FALSE;
+ }
+ else
+ {
+ if (*s == '>')
+ {
+ s++;
+ break;
+ }
+ else if (*s == '\'') inquote = EINA_TRUE;
+ else if (*s == '\"') indblquote = EINA_TRUE;
+ }
+ }
+ }
+ else backslash = EINA_FALSE;
+ }
+ return s - txt;
+}
+
+static char *
+_path_get(const char *txt)
{
- unsigned int i;
+ Eina_Strbuf *buf;
+ char *ret;
+ const char *s;
+ Eina_Bool backslash = EINA_FALSE;
+ Eina_Bool inquote = EINA_FALSE, indblquote = EINA_FALSE;
- if (c[1] != '>') return NULL;
- for (i = 0; i < EINA_C_ARRAY_LENGTH(tags); i++)
- if (tags[i][1] == c[0]) return tags[i];
- return NULL;
+ if (txt[0] == '>') return NULL;
+
+ buf = eina_strbuf_new();
+ if (!buf) return NULL;
+
+ for (s = txt; *s; s++)
+ {
+ if (!backslash)
+ {
+ if (*s == '\\') backslash = EINA_TRUE;
+ else
+ {
+ if (inquote)
+ {
+ if (*s == '\'') inquote = EINA_FALSE;
+ else eina_strbuf_append_char(buf, *s);
+ }
+ else if (indblquote)
+ {
+ if (*s == '"') indblquote = EINA_FALSE;
+ else eina_strbuf_append_char(buf, *s);
+ }
+ else
+ {
+ if (*s == '>') break;
+ else if (*s == ' ') break;
+ else if (*s == '\'') inquote = EINA_TRUE;
+ else if (*s == '\"') indblquote = EINA_TRUE;
+ else eina_strbuf_append_char(buf, *s);
+ }
+ }
+ }
+ else
+ {
+ eina_strbuf_append_char(buf, *s);
+ backslash = EINA_FALSE;
+ }
+ }
+ ret = eina_strbuf_string_steal(buf);
+ eina_strbuf_free(buf);
+ return ret;
}
-char *
+static char *
_nedje_text_escape(const char *text)
{
Eina_Strbuf *txt;
char *ret;
const char *text_end;
- size_t text_len;
- Eina_Array *arr;
- const char *cur_tag = NULL;
+ int taglen;
if (!text) return NULL;
txt = eina_strbuf_new();
- text_len = strlen(text);
- arr = eina_array_new(3);
+ if (!txt) return NULL;
+
+ text_end = text + strlen(text);
- text_end = text + text_len;
while (text < text_end)
{
- int advance;
-
- if ((text[0] == '<') && text[1])
+ taglen = _tag_len(text);
+ if (taglen == 0)
{
- const char *tag, *popped;
- Eina_Bool closing = EINA_FALSE;
-
- if (text[1] == '/') //closing tag
+ eina_strbuf_append_char(txt, text[0]);
+ text++;
+ }
+ else
+ {
+ if (!strncmp(text, "<b>", 3)) eina_strbuf_append(txt, "<b>");
+ else if (!strncmp(text, "</b>", 4)) eina_strbuf_append(txt, "</b>");
+ else if (!strncmp(text, "<i>", 3)) eina_strbuf_append(txt, "<i>");
+ else if (!strncmp(text, "</i>", 4)) eina_strbuf_append(txt, "</i>");
+ else if (!strncmp(text, "<u>", 3)) eina_strbuf_append(txt, "<u>");
+ else if (!strncmp(text, "</u>", 4)) eina_strbuf_append(txt, "</u>");
+ else if (!strncmp(text, "<a ", 3))
{
- closing = EINA_TRUE;
- tag = _get_tag(text + 2);
+ eina_strbuf_append(txt, "<link>");
+ eina_strbuf_append_n(txt, text, taglen);
}
- else
- tag = _get_tag(text + 1);
- if (closing)
+ else if (!strncmp(text, "</a>", 3))
{
- if (cur_tag && (tag != cur_tag))
- {
- /* tag mismatch: autoclose all failure tags
- * not technically required by the spec,
- * but it makes me feel better about myself
- */
- do
- {
- popped = eina_array_pop(arr);
- if (eina_array_count(arr))
- cur_tag = eina_array_data_get(arr, eina_array_count(arr) - 1);
- else
- cur_tag = NULL;
- eina_strbuf_append_printf(txt, "</%c>", popped[1]);
- } while (cur_tag && (popped != tag));
- advance = 4;
- }
- else if (cur_tag)
- {
- /* tag match: just pop */
- popped = eina_array_pop(arr);
- if (eina_array_count(arr))
- cur_tag = eina_array_data_get(arr, eina_array_count(arr) - 1);
- else
- cur_tag = NULL;
- eina_strbuf_append_printf(txt, "</%c>", popped[1]);
- advance = 4;
- }
- else
- {
- /* no current tag: escape */
- advance = _text_escape(txt, text);
- }
+ eina_strbuf_append(txt, "</a></link>");
}
- else
+ else if (!strncmp(text, "<img src="" 9))
{
- if (tag)
+ Evas_Object *o;
+ int w = 0, h = 0;
+ char *path;
+
+ path = _path_get(text + 9);
+ if ((path) && (strlen(path) > 0))
{
- cur_tag = tag;
- eina_array_push(arr, tag);
- eina_strbuf_append_printf(txt, "<%c>", tag[1]);
- advance = 3;
+ o = evas_object_image_add(e_comp->evas);
+ evas_object_image_file_set(o, path, NULL);
+ evas_object_image_size_get(o, &w, &h);
+ printf("NOT: imgpath=%s %ix%i\n", path, w, h);
+ if ((w > 0) && (h > 0))
+ {
+ double neww = w, newh = h;
+
+ if (neww > 200.0)
+ {
+ double oldw = neww;
+
+ neww = 200.0;
+ newh = (newh * neww) / oldw;
+ }
+ if (newh > 100.0)
+ {
+ double oldh = newh;
+
+ newh = 100.0;
+ neww = (neww * newh) / oldh;
+ }
+ neww *= e_scale;
+ newh *= e_scale;
+ w = neww + 0.5;
+ h = newh + 0.5;
+ eina_strbuf_append_printf
+ (txt, "<item absize=%ix%i href="" w, h);
+ eina_strbuf_append_n(txt, text + 9, taglen - 9);
+ eina_strbuf_append(txt, "</item>");
+ }
+ evas_object_del(o);
}
- else
- advance = _text_escape(txt, text);
+ free(path);
}
+ text += taglen;
}
- else if (text[0] == '&')
- {
- const char *s;
-
- s = strchr(text, ';');
- if (s)
- s = evas_textblock_escape_string_range_get(text, s + 1);
- if (s)
- {
- eina_strbuf_append_char(txt, text[0]);
- advance = 1;
- }
- else
- advance = _text_escape(txt, text);
- }
- else
- advance = _text_escape(txt, text);
-
- text += advance;
}
- eina_array_free(arr);
ret = eina_strbuf_string_steal(txt);
+ printf("NOT: body -> [%s]\n", ret);
eina_strbuf_free(txt);
return ret;
}
@@ -231,28 +415,30 @@ notify_cb(const Eldbus_Service_Interface *iface EINA_UNUSED, const Eldbus_Messag
E_Notification_Notify *n;
Eldbus_Message_Iter *actions_iter, *hints_iter;
Eldbus_Message *reply;
- char *txt;
+ char *txt, *txt2;
+ int num;
- if (!n_data->notify_cb)
- return NULL;
+ if (!n_data->notify_cb) return NULL;
n = E_OBJECT_ALLOC(E_Notification_Notify, E_NOTIFICATION_TYPE, _notification_free);
n->urgency = E_NOTIFICATION_NOTIFY_URGENCY_NORMAL;
- if (!eldbus_message_arguments_get(msg, "susssasa{sv}i", &n->app_name,
- &n->replaces_id, &n->icon.icon, &n->summary,
- &n->body, &actions_iter, &hints_iter,
- &n->timeout))
+ if (!eldbus_message_arguments_get(msg, "susssasa{sv}i",
+ &n->app_name, &n->replaces_id,
+ &n->icon.icon, &n->summary, &n->body,
+ &actions_iter, &hints_iter, &n->timeout))
{
ERR("Reading message.");
e_object_del(E_OBJECT(n));
return NULL;
}
if (e_screensaver_on_get() && e_config->screensaver_wake_on_notify)
- {
+ { // XXX: this is an attempt to wake the screen? should be an option
int x, y;
+
ecore_evas_pointer_xy_get(e_comp->ee, &x, &y);
ecore_evas_pointer_warp(e_comp->ee, x, y);
}
+ // walk hints
eldbus_message_iter_dict_iterate(hints_iter, "sv", hints_dict_iter, n);
n->app_name = eina_stringshare_add(n->app_name);
n->icon.icon = eina_stringshare_add(n->icon.icon);
@@ -261,22 +447,43 @@ notify_cb(const Eldbus_Service_Interface *iface EINA_UNUSED, const Eldbus_Messag
n->body = eina_stringshare_add(txt);
free(txt);
+ num = 0;
+ while (eldbus_message_iter_get_and_next(actions_iter, 's', &txt))
+ {
+ if (eldbus_message_iter_get_and_next(actions_iter, 's', &txt2))
+ { // XXX: add actions to notification
+ E_Notification_Notify_Action *actions;
+
+ printf("NOT: act=[%s] [%s]\n", txt, txt2);
+ num++;
+ actions = realloc(n->actions, (num + 1) * sizeof(E_Notification_Notify));
+ if (actions)
+ {
+ n->actions = actions;
+ n->actions[num - 1].action = ""
+ n->actions[num - 1].label = eina_stringshare_add(txt2);
+ n->actions[num].action = ""
+ n->actions[num].label = NULL;
+ }
+ }
+ }
+
e_object_ref(E_OBJECT(n));
n->id = n_data->notify_cb(n_data->data, n);
reply = eldbus_message_method_return_new(msg);
eldbus_message_arguments_append(reply, "u", n->id);
e_object_unref(E_OBJECT(n));
+
return reply;
}
static Eldbus_Message *
close_notification_cb(const Eldbus_Service_Interface *iface EINA_UNUSED, const Eldbus_Message *msg)
{
- unsigned id;
- if (!eldbus_message_arguments_get(msg, "u", &id))
- return NULL;
- if (n_data->close_cb)
- n_data->close_cb(n_data->data, id);
+ unsigned int id;
+
+ if (!eldbus_message_arguments_get(msg, "u", &id)) return NULL;
+ if (n_data->close_cb) n_data->close_cb(n_data->data, id);
return eldbus_message_method_return_new(msg);
}
@@ -291,8 +498,10 @@ capabilities_cb(const Eldbus_Service_Interface *iface EINA_UNUSED, const Eldbus_
eldbus_message_iter_arguments_append(main_iter, "as", &array);
for (i = 0; n_data->server_info->capabilities[i]; i++)
- eldbus_message_iter_arguments_append(array, "s",
- n_data->server_info->capabilities[i]);
+ {
+ eldbus_message_iter_arguments_append
+ (array, "s", n_data->server_info->capabilities[i]);
+ }
eldbus_message_iter_container_close(main_iter, array);
return reply;
}
@@ -301,23 +510,32 @@ static Eldbus_Message *
server_info_cb(const Eldbus_Service_Interface *iface EINA_UNUSED, const Eldbus_Message *msg)
{
Eldbus_Message *reply = eldbus_message_method_return_new(msg);
- eldbus_message_arguments_append(reply, "ssss", n_data->server_info->name,
- n_data->server_info->vendor,
- n_data->server_info->version,
- n_data->server_info->spec_version);
+ eldbus_message_arguments_append(reply, "ssss",
+ n_data->server_info->name,
+ n_data->server_info->vendor,
+ n_data->server_info->version,
+ n_data->server_info->spec_version);
return reply;
}
static const Eldbus_Method methods[] = {
{ "Notify",
- ELDBUS_ARGS({"s", "app_name"}, {"u", "replaces_id"}, {"s", "app_icon"}, {"s", "summary"}, {"s", "body"}, {"as", "actions"}, {"a{sv}", "hints"}, {"i", "expire_timeout"}),
- ELDBUS_ARGS({"u", "id"}), notify_cb, 0 },
- { "CloseNotification", ELDBUS_ARGS({"u", "id"}), NULL, close_notification_cb, 0 },
- { "GetCapabilities", NULL, ELDBUS_ARGS({"as", "capabilities"}),
- capabilities_cb, 0 },
- { "GetServerInformation", NULL,
- ELDBUS_ARGS({"s", "name"}, {"s", "vendor"}, {"s", "version"}, {"s", "spec_version"}),
- server_info_cb, 0 },
+ ELDBUS_ARGS({"s", "app_name"}, {"u", "replaces_id"}, {"s", "app_icon"}, {"s", "summary"}, {"s", "body"}, {"as", "actions"}, {"a{sv}", "hints"}, {"i", "expire_timeout"}),
+ ELDBUS_ARGS({"u", "id"}),
+ notify_cb, 0 },
+ { "CloseNotification",
+ ELDBUS_ARGS({"u", "id"}),
+ NULL,
+ close_notification_cb, 0 },
+ { "GetCapabilities",
+ NULL,
+ ELDBUS_ARGS({"as", "capabilities"}),
+ capabilities_cb, 0 },
+ { "GetServerInformation",
+ NULL,
+ ELDBUS_ARGS({"s", "name"}, {"s", "vendor"}, {"s", "version"}, {"s", "spec_version"}),
+ server_info_cb, 0 },
+
{ NULL, NULL, NULL, NULL, 0 }
};
@@ -332,6 +550,7 @@ static const Eldbus_Signal signals[] = {
{ "NotificationClosed", ELDBUS_ARGS({"u", "id"}, {"u", "reason"}), 0 },
[SIGNAL_ACTION_INVOKED] =
{ "ActionInvoked", ELDBUS_ARGS({"u", "id"}, {"s", "action_key"}), 0 },
+
{ NULL, NULL, 0}
};
@@ -347,8 +566,8 @@ E_API Eina_Bool
e_notification_server_register(const E_Notification_Server_Info *server_info, E_Notification_Notify_Cb n_cb, E_Notification_Close_Cb close_cb, const void *data)
{
EINA_SAFETY_ON_NULL_RETURN_VAL(server_info, EINA_FALSE);
- if (n_data)
- return EINA_FALSE;
+
+ if (n_data) return EINA_FALSE;
n_data = calloc(1, sizeof(Notification_Data));
EINA_SAFETY_ON_NULL_RETURN_VAL(n_data, EINA_FALSE);
@@ -359,8 +578,7 @@ e_notification_server_register(const E_Notification_Server_Info *server_info, E_
n_data->data = "" *)data;
n_data->server_info = server_info;
eldbus_name_request(n_data->conn, BUS,
- ELDBUS_NAME_REQUEST_FLAG_REPLACE_EXISTING, NULL, NULL);
-
+ ELDBUS_NAME_REQUEST_FLAG_REPLACE_EXISTING, NULL, NULL);
return EINA_TRUE;
}
@@ -381,7 +599,17 @@ e_notification_notify_close(E_Notification_Notify *notify, E_Notification_Notify
EINA_SAFETY_ON_NULL_RETURN(notify);
EINA_SAFETY_ON_FALSE_RETURN(reason <= E_NOTIFICATION_NOTIFY_CLOSED_REASON_UNDEFINED);
eldbus_service_signal_emit(n_data->iface, SIGNAL_NOTIFICATION_CLOSED,
- notify->id, reason);
+ notify->id, reason);
+}
+
+E_API void
+e_notification_notify_action(E_Notification_Notify *notify, const char *action)
+{
+ EINA_SAFETY_ON_NULL_RETURN(n_data);
+ EINA_SAFETY_ON_NULL_RETURN(notify);
+ if (!action) action = ""
+ eldbus_service_signal_emit(n_data->iface, SIGNAL_ACTION_INVOKED,
+ notify->id, action);
}
E_API Evas_Object *
@@ -491,6 +719,7 @@ notification_client_dbus_send(E_Notification_Notify *notify, E_Notification_Clie
{
Eldbus_Message_Iter *st, *data_iter;
int i;
+
eldbus_message_iter_arguments_append(hints, "{sv}", &entry);
eldbus_message_iter_arguments_append(entry, "s", "image-data");
var = eldbus_message_iter_container_new(entry, 'v', "(iiibiiay)");
@@ -533,8 +762,7 @@ notification_client_dbus_send(E_Notification_Notify *notify, E_Notification_Clie
p = eldbus_connection_send(conn, msg, client_notify_cb, data, 5000);
EINA_SAFETY_ON_NULL_GOTO(p, error);
- if (cb)
- eldbus_pending_data_set(p, "cb", cb);
+ if (cb) eldbus_pending_data_set(p, "cb", cb);
eldbus_pending_data_set(p, "conn", conn);
return EINA_TRUE;
@@ -547,8 +775,7 @@ error:
static void
normalize_notify(E_Notification_Notify *notify)
{
- if (!notify->timeout)
- notify->timeout = -1;
+ if (!notify->timeout) notify->timeout = -1;
}
E_API Eina_Bool
@@ -562,11 +789,12 @@ e_notification_client_send(E_Notification_Notify *notify, E_Notification_Client_
if (!n_data)
{
- fprintf(stderr, "UNHANDLED NOTIFICATION:\nSummary: %s\nBody: %s\n", notify->summary, notify->body);
+ fprintf(stderr, "UNHANDLED NOTIFICATION:\nSummary: %s\nBody: %s\n",
+ notify->summary, notify->body);
return notification_client_dbus_send(notify, cb, data);
}
- //local
+ // local
copy = malloc(sizeof(E_Notification_Notify));
EINA_SAFETY_ON_NULL_RETURN_VAL(copy, EINA_FALSE);
memcpy(copy, notify, sizeof(E_Notification_Notify));
@@ -579,8 +807,7 @@ e_notification_client_send(E_Notification_Notify *notify, E_Notification_Client_
copy->icon.icon_path = eina_stringshare_add(notify->icon.icon_path);
id = n_data->notify_cb(n_data->data, copy);
- if (cb)
- cb((void *)data, id);
+ if (cb) cb((void *)data, id);
return EINA_TRUE;
}
diff --git a/src/bin/e_notification.h b/src/bin/e_notification.h
index 46228982d..c93d553f4 100644
--- a/src/bin/e_notification.h
+++ b/src/bin/e_notification.h
@@ -7,35 +7,39 @@
typedef enum _E_Notification_Notify_Urgency
{
- E_NOTIFICATION_NOTIFY_URGENCY_LOW,
- E_NOTIFICATION_NOTIFY_URGENCY_NORMAL,
- E_NOTIFICATION_NOTIFY_URGENCY_CRITICAL
+ E_NOTIFICATION_NOTIFY_URGENCY_LOW = 0,
+ E_NOTIFICATION_NOTIFY_URGENCY_NORMAL = 1,
+ E_NOTIFICATION_NOTIFY_URGENCY_CRITICAL = 2
} E_Notification_Notify_Urgency;
typedef enum _E_Notification_Notify_Closed_Reason
{
- E_NOTIFICATION_NOTIFY_CLOSED_REASON_EXPIRED, /** The notification expired. */
- E_NOTIFICATION_NOTIFY_CLOSED_REASON_DISMISSED, /** The notification was dismissed by the user. */
- E_NOTIFICATION_NOTIFY_CLOSED_REASON_REQUESTED, /** The notification was closed by a call to CloseNotification method. */
- E_NOTIFICATION_NOTIFY_CLOSED_REASON_UNDEFINED /** Undefined/reserved reasons. */
+ E_NOTIFICATION_NOTIFY_CLOSED_REASON_EXPIRED = 1,
+ E_NOTIFICATION_NOTIFY_CLOSED_REASON_DISMISSED = 2,
+ E_NOTIFICATION_NOTIFY_CLOSED_REASON_REQUESTED = 3,
+ E_NOTIFICATION_NOTIFY_CLOSED_REASON_UNDEFINED = 4
} E_Notification_Notify_Closed_Reason;
+typedef struct _E_Notification_Notify_Action
+{
+ const char *action;
+ const char *label;
+} E_Notification_Notify_Action;
+
typedef struct _E_Notification_Notify
{
E_Object e_obj_inherit;
unsigned int id;
const char *app_name;
- unsigned replaces_id;
+ unsigned int replaces_id;
const char *summary;
const char *body;
- int timeout;
+ int timeout; // time in ms
E_Notification_Notify_Urgency urgency;
- struct
- {
+ struct {
const char *icon;
const char *icon_path;
- struct
- {
+ struct {
int width;
int height;
int rowstride;
@@ -46,6 +50,17 @@ typedef struct _E_Notification_Notify
int data_size;
} raw;
} icon;
+ const char *category;
+ const char *desktop_entry;
+ const char *sound_file;
+ const char *sound_name;
+ int x, y;
+ Eina_Bool have_xy;
+ Eina_Bool icon_actions;
+ Eina_Bool resident;
+ Eina_Bool suppress_sound;
+ Eina_Bool transient;
+ E_Notification_Notify_Action *actions;
} E_Notification_Notify;
typedef unsigned int (*E_Notification_Notify_Cb)(void *data, E_Notification_Notify *n);
@@ -80,7 +95,9 @@ E_API Evas_Object *e_notification_notify_raw_image_get(E_Notification_Notify *no
//client
typedef void (*E_Notification_Client_Send_Cb)(void *data, unsigned int id);
+
E_API Eina_Bool e_notification_client_send(E_Notification_Notify *notify, E_Notification_Client_Send_Cb cb, const void *data);
+E_API void e_notification_notify_action(E_Notification_Notify *notify, const char *action);
E_API Eina_Bool e_notification_util_send(const char *summary, const char *body);
#endif
diff --git a/src/modules/notification/e_mod_main.c b/src/modules/notification/e_mod_main.c
index e8afe13b4..ec3c9eb8c 100644
--- a/src/modules/notification/e_mod_main.c
+++ b/src/modules/notification/e_mod_main.c
@@ -28,11 +28,20 @@ _notification_notify(E_Notification_Notify *n)
E_API E_Module_Api e_modapi = {E_MODULE_API_VERSION, "Notification"};
static const E_Notification_Server_Info server_info = {
- .name = "e17",
- .vendor = "enlightenment.org",
- .version = "0.17",
+ .name = "Notification Service",
+ .vendor = "Enlightenment",
+ .version = PACKAGE_VERSION,
.spec_version = "1.2",
- .capabilities = { "body", "body-markup", NULL }
+ .capabilities = {
+ "body", "body-markup",
+ "body-hyperlinks", "body-images",
+ "actions", "action-icons",
+// "icon-multi",
+// or
+// "icon-static",
+ "persistence",
+// "sound",
+ NULL }
};
/* Callbacks */
diff --git a/src/modules/notification/e_mod_main.h b/src/modules/notification/e_mod_main.h
index 714448166..094e23847 100644
--- a/src/modules/notification/e_mod_main.h
+++ b/src/modules/notification/e_mod_main.h
@@ -31,34 +31,37 @@ typedef enum
struct _Config
{
- E_Config_Dialog *cfd;
-
- int version;
- int show_low;
- int show_normal;
- int show_critical;
- int force_timeout;
- int ignore_replacement;
- Popup_Display_Policy dual_screen;
- float timeout;
- Popup_Corner corner;
-
- Eina_List *popups;
- unsigned int next_id;
+ E_Config_Dialog *cfd;
+
+ int version;
+ int show_low;
+ int show_normal;
+ int show_critical;
+ int force_timeout;
+ int ignore_replacement;
+ Popup_Display_Policy dual_screen;
+ float timeout;
+ Popup_Corner corner;
+
+ Eina_List *popups;
+ unsigned int next_id;
};
struct _Popup_Data
{
- unsigned id;
- E_Notification_Notify *notif;
- Evas_Object *win;
- Eina_List *mirrors;
- Evas *e;
- Evas_Object *theme;
- const char *app_name;
- Evas_Object *app_icon;
- Ecore_Timer *timer;
- Eina_Bool pending E_BITFIELD;
+ unsigned id;
+ E_Notification_Notify *notif;
+ Evas_Object *win;
+ Eina_List *mirrors;
+ Evas *e;
+ Evas_Object *theme;
+ const char *app_name;
+ Evas_Object *app_icon;
+ Evas_Object *desktop_icon;
+ Evas_Object *action_box;
+ Eina_List *actions;
+ Ecore_Timer *timer;
+ Eina_Bool pending E_BITFIELD;
};
diff --git a/src/modules/notification/e_mod_popup.c b/src/modules/notification/e_mod_popup.c
index 444bc551f..6cf2ccc54 100644
--- a/src/modules/notification/e_mod_popup.c
+++ b/src/modules/notification/e_mod_popup.c
@@ -239,6 +239,61 @@ _notification_theme_cb_find(Popup_Data *popup,
}
}
+static void
+_notification_theme_cb_anchor(Popup_Data *popup EINA_UNUSED,
+ Evas_Object *obj EINA_UNUSED,
+ const char *emission,
+ const char *source EINA_UNUSED)
+{
+ if (!strncmp(emission, "anchor,mouse,clicked,1,",
+ strlen("anchor,mouse,clicked,1,")))
+ {
+ const char *href = "" + strlen("anchor,mouse,clicked,1,");
+ Eina_Strbuf *buf = eina_strbuf_new();
+
+ if (buf)
+ {
+ const char *s;
+
+ eina_strbuf_append(buf, href);
+ s = eina_strbuf_string_get(buf);
+ if ((s) && (*s == '"'))
+ {
+ eina_strbuf_remove(buf, 0, 1);
+ s = eina_strbuf_string_get(buf);
+ if ((s) && (strlen(s) > 0) && (s[strlen(s) - 1] == '"'))
+ eina_strbuf_replace_last(buf, "\"", "");
+ }
+ if ((s) && (*s == '\''))
+ {
+ eina_strbuf_remove(buf, 0, 1);
+ s = eina_strbuf_string_get(buf);
+ if ((s) && (strlen(s) > 0) && (s[strlen(s) - 1] == '\''))
+ eina_strbuf_replace_last(buf, "'", "");
+ }
+ printf("NOT: clicked=[%s]\n", eina_strbuf_string_get(buf));
+ e_util_open(eina_strbuf_string_get(buf), NULL);
+ eina_strbuf_free(buf);
+ }
+ }
+}
+
+static void
+_notification_theme_cb_action(Popup_Data *popup,
+ Evas_Object *obj,
+ const char *emission EINA_UNUSED,
+ const char *source EINA_UNUSED)
+{
+ const char *action = "" "action");
+
+ if (action)
+ {
+ printf("NOT: action="" action);
+ e_notification_notify_action(popup->notif, action);
+ }
+}
+
+
static void
_notification_popup_place_coords_get(int zw, int zh, int ow, int oh, int pos, int *x, int *y)
{
@@ -269,6 +324,22 @@ _notification_popup_del_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EIN
popup->win = NULL;
}
+static Evas_Object *
+_cb_item_provider(void *data, Evas_Object *obj EINA_UNUSED, const char *part, const char *item)
+{
+ Popup_Data *popup = data;
+
+ printf("NOT: PROVIDER.... [%s] item: [%s]\n", part, item);
+// if (!strcmp(part, "notification.textblock.message"))
+ {
+ Evas_Object *o = e_icon_add(popup->e);
+
+ e_icon_file_set(o, item);
+ return o;
+ }
+ return NULL;
+}
+
static Popup_Data *
_notification_popup_new(E_Notification_Notify *n, unsigned id)
{
@@ -307,6 +378,7 @@ _notification_popup_new(E_Notification_Notify *n, unsigned id)
/* Setup the theme */
popup->theme = edje_object_add(popup->e);
+ edje_object_item_provider_set(popup->theme, _cb_item_provider, popup);
e_theme_edje_object_set(popup->theme,
"base/theme/modules/notification",
"e/modules/notification/main");
@@ -318,14 +390,17 @@ _notification_popup_new(E_Notification_Notify *n, unsigned id)
evas_object_event_callback_add(popup->win, EVAS_CALLBACK_DEL, _notification_popup_del_cb, popup);
edje_object_signal_callback_add
- (popup->theme, "notification,deleted", "theme",
+ (popup->theme, "notification,deleted", "*",
(Edje_Signal_Cb)_notification_theme_cb_deleted, popup);
edje_object_signal_callback_add
- (popup->theme, "notification,close", "theme",
+ (popup->theme, "notification,close", "*",
(Edje_Signal_Cb)_notification_theme_cb_close, popup);
edje_object_signal_callback_add
- (popup->theme, "notification,find", "theme",
+ (popup->theme, "notification,find", "*",
(Edje_Signal_Cb)_notification_theme_cb_find, popup);
+ edje_object_signal_callback_add
+ (popup->theme, "anchor,mouse,clicked,1,*", "notification.textblock.message",
+ (Edje_Signal_Cb)_notification_theme_cb_anchor, popup);
_notification_popup_refresh(popup);
next_pos = _notification_popup_place(popup, next_pos);
@@ -386,17 +461,35 @@ _notification_popup_refresh(Popup_Data *popup)
const char *app_icon_max;
int w, h, width = 80, height = 80;
E_Zone *zone;
+ Evas_Object *o;
if (!popup) return;
popup->app_name = popup->notif->app_name;
+ EINA_LIST_FREE(popup->actions, o)
+ {
+ evas_object_del(o);
+ }
+ if (popup->action_box)
+ {
+ e_comp_object_util_del_list_remove(popup->win, popup->action_box);
+ E_FREE_FUNC(popup->action_box, evas_object_del);
+ edje_object_signal_emit(popup->theme, "e,state,actions,hide", "e");
+ }
+
if (popup->app_icon)
{
e_comp_object_util_del_list_remove(popup->win, popup->app_icon);
E_FREE_FUNC(popup->app_icon, evas_object_del);
}
+ if (popup->desktop_icon)
+ {
+ e_comp_object_util_del_list_remove(popup->win, popup->desktop_icon);
+ E_FREE_FUNC(popup->desktop_icon, evas_object_del);
+ }
+
app_icon_max = edje_object_data_get(popup->theme, "app_icon_max");
if (app_icon_max)
{
@@ -452,7 +545,7 @@ _notification_popup_refresh(Popup_Data *popup)
icon_path = new_path;
else
{
- Evas_Object *o = e_icon_add(popup->e);
+ o = e_icon_add(popup->e);
if (!e_util_icon_theme_set(o, icon_path))
evas_object_del(o);
else
@@ -510,12 +603,79 @@ _notification_popup_refresh(Popup_Data *popup)
popup->app_icon);
edje_object_signal_emit(popup->theme, "notification,icon", "notification");
+ if ((popup->notif->desktop_entry) &&
+ (edje_object_part_exists(popup->theme, "notification.swallow.desktop_icon")))
+ {
+ Efreet_Desktop *desktop;
+ unsigned int size;
+ const char *icon_path;
+ char buf[1024];
+
+ snprintf(buf, sizeof(buf), "%s.desktop", popup->notif->desktop_entry);
+ desktop = efreet_util_desktop_file_id_find(buf);
+ if ((desktop) && (desktop->icon))
+ {
+ size = e_util_icon_size_normalize(width * e_scale);
+ icon_path = efreet_icon_path_find(e_config->icon_theme,
+ desktop->icon, size);
+ efreet_desktop_free(desktop);
+
+ o = e_icon_add(popup->e);
+ if (!e_util_icon_theme_set(o, icon_path))
+ evas_object_del(o);
+ else
+ {
+ popup->desktop_icon = o;
+ edje_object_part_swallow(popup->theme,
+ "notification.swallow.desktop_icon",
+ popup->desktop_icon);
+ evas_object_show(o);
+ e_comp_object_util_del_list_append(popup->win, o);
+ }
+ }
+ }
/* Fill up the event message */
_notification_format_message(popup);
+ if (popup->notif->actions)
+ {
+ int i;
+
+ o = popup->action_box = elm_box_add(e_comp->elm);
+ elm_box_homogeneous_set(o, EINA_TRUE);
+ elm_box_horizontal_set(o, EINA_TRUE);
+ e_comp_object_util_del_list_append(popup->win, o);
+ for (i = 0; popup->notif->actions[i].action; i++)
+ {
+ o = edje_object_add(popup->e);
+ e_theme_edje_object_set(o,
+ "base/theme/modules/notification",
+ "e/modules/notification/action");
+ evas_object_data_set(o, "action", popup->notif->actions[i].action);
+ edje_object_part_text_unescaped_set(o, "e.text.label",
+ popup->notif->actions[i].label);
+ edje_object_signal_callback_add
+ (o, "e,action,clicked", "e",
+ (Edje_Signal_Cb)_notification_theme_cb_action, popup);
+ edje_object_size_min_calc(o, &w, &h);
+ evas_object_size_hint_min_set(o, w, h);
+ printf("NOT: act %ix%i\n", w, h);
+ elm_box_pack_end(popup->action_box, o);
+ evas_object_show(o);
+ }
+// evas_smart_objects_calculate(popup->e);
+// edje_message_signal_process();
+ evas_smart_objects_calculate(popup->e);
+ evas_object_size_hint_min_get(popup->action_box, &w, &h);
+ printf("NOT: actbox %ix%i\n", w, h);
+ edje_object_part_swallow(popup->theme, "notification.swallow.actions", popup->action_box);
+ edje_object_signal_emit(popup->theme, "e,state,actions,show", "e");
+ }
+
/* Compute the new size of the popup */
edje_object_calc_force(popup->theme);
edje_object_size_min_calc(popup->theme, &w, &h);
+ printf("NOT: min %ix%i\n", w, h);
if ((zone = e_comp_object_util_zone_get(popup->win)))
{
w = MIN(w, zone->w / 2);
@@ -585,13 +745,18 @@ _notification_format_message(Popup_Data *popup)
{
Evas_Object *o = popup->theme;
Eina_Strbuf *buf = eina_strbuf_new();
+
+ printf("NOT: set message... [%s]\n", popup->notif->body);
edje_object_part_text_unescaped_set(o, "notification.text.title",
- popup->notif->summary);
+ popup->notif->summary);
/* FIXME: Filter to only include allowed markup? */
/* We need to replace \n with <ps/>. FIXME: We need to handle all the
* newline kinds, and paragraph separator. ATM this will suffice. */
eina_strbuf_append(buf, popup->notif->body);
eina_strbuf_replace_all(buf, "\n", "<br/>");
+ // message is thge shadow sizer part
+ edje_object_part_text_set(o, "message",
+ eina_strbuf_string_get(buf));
edje_object_part_text_set(o, "notification.textblock.message",
eina_strbuf_string_get(buf));
eina_strbuf_free(buf);
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.