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
This commit is contained in:
Carsten Haitzler 2022-06-18 00:44:17 +01:00
parent 1933f745f2
commit b23eedae98
5 changed files with 629 additions and 208 deletions

View File

@ -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 = malloc(sizeof(char) * 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);
}
else if (!strcmp(key, "urgency"))
{
unsigned char urgency;
eldbus_message_iter_arguments_get(var, "y", &urgency);
if (urgency < 3)
n->urgency = urgency;
printf("NOT: image-data=%ix%i,a=%i\n", 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;
printf("NOT: urgency=%i\n", n->urgency);
// 0=low, 1=normal, 2=critical
}
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"))
{
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++)
{
unsigned int i;
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 (!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;
}
char *
static char *
_path_get(const char *txt)
{
Eina_Strbuf *buf;
char *ret;
const char *s;
Eina_Bool backslash = EINA_FALSE;
Eina_Bool inquote = EINA_FALSE, indblquote = EINA_FALSE;
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;
}
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])
{
const char *tag, *popped;
Eina_Bool closing = EINA_FALSE;
if (text[1] == '/') //closing tag
{
closing = EINA_TRUE;
tag = _get_tag(text + 2);
}
else
tag = _get_tag(text + 1);
if (closing)
{
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);
}
}
else
{
if (tag)
{
cur_tag = tag;
eina_array_push(arr, tag);
eina_strbuf_append_printf(txt, "<%c>", tag[1]);
advance = 3;
}
else
advance = _text_escape(txt, text);
}
}
else if (text[0] == '&')
{
const char *s;
s = strchr(text, ';');
if (s)
s = evas_textblock_escape_string_range_get(text, s + 1);
if (s)
taglen = _tag_len(text);
if (taglen == 0)
{
eina_strbuf_append_char(txt, text[0]);
advance = 1;
text++;
}
else
advance = _text_escape(txt, text);
{
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))
{
eina_strbuf_append(txt, "<link>");
eina_strbuf_append_n(txt, text, taglen);
}
else
advance = _text_escape(txt, text);
else if (!strncmp(text, "</a>", 3))
{
eina_strbuf_append(txt, "</a></link>");
}
else if (!strncmp(text, "<img src=", 9))
{
Evas_Object *o;
int w = 0, h = 0;
char *path;
text += advance;
path = _path_get(text + 9);
if ((path) && (strlen(path) > 0))
{
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);
}
free(path);
}
text += taglen;
}
}
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 = eina_stringshare_add(txt);
n->actions[num - 1].label = eina_stringshare_add(txt2);
n->actions[num].action = NULL;
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,7 +510,8 @@ 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,
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);
@ -311,13 +521,21 @@ server_info_cb(const Eldbus_Service_Interface *iface EINA_UNUSED, const Eldbus_M
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"}),
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,
{ "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);
@ -360,7 +579,6 @@ e_notification_server_register(const E_Notification_Server_Info *server_info, E_
n_data->server_info = server_info;
eldbus_name_request(n_data->conn, BUS,
ELDBUS_NAME_REQUEST_FLAG_REPLACE_EXISTING, NULL, NULL);
return EINA_TRUE;
}
@ -384,6 +602,16 @@ e_notification_notify_close(E_Notification_Notify *notify, E_Notification_Notify
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 *
e_notification_notify_raw_image_get(E_Notification_Notify *notify, Evas *evas)
{
@ -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,7 +789,8 @@ 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);
}
@ -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;
}

View File

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

View File

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

View File

@ -57,6 +57,9 @@ struct _Popup_Data
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;
};

View File

@ -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 = emission + 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 = evas_object_data_get(obj, "action");
if (action)
{
printf("NOT: action=[%s]\n", 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,6 +745,8 @@ _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);
/* FIXME: Filter to only include allowed markup? */
@ -592,6 +754,9 @@ _notification_format_message(Popup_Data *popup)
* 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);