1 #ifdef HAVE_CONFIG_H
2 # include "elementary_config.h"
3 #endif
4 
5 #include <Elementary.h>
6 
7 #include "elm_priv.h"
8 
9 #include "elm_sys_notify_interface_eo.h"
10 #include "elm_sys_notify_eo.h"
11 #include "elm_sys_notify_dbus_eo.h"
12 #include "elm_sys_notify_dbus_eo.legacy.h"
13 
14 #define MY_CLASS ELM_SYS_NOTIFY_DBUS_CLASS
15 
16 #define OBJ       "/org/freedesktop/Notifications"
17 #define BUS       "org.freedesktop.Notifications"
18 #define INTERFACE "org.freedesktop.Notifications"
19 
20 static Eldbus_Connection *_elm_sysnotif_conn  = NULL;
21 static Eldbus_Object     *_elm_sysnotif_obj   = NULL;
22 static Eldbus_Proxy      *_elm_sysnotif_proxy = NULL;
23 
24 static Eina_Bool _has_markup = EINA_FALSE;
25 
26 typedef struct _Elm_Sys_Notify_Send_Data
27 {
28    Elm_Sys_Notify_Send_Cb cb;
29    const void *data;
30 } Elm_Sys_Notify_Send_Data;
31 
32 static void
_elm_sys_notify_marshal_dict_byte(Eldbus_Message_Iter * array,const char * key,const char value)33 _elm_sys_notify_marshal_dict_byte(Eldbus_Message_Iter *array,
34                                   const char *key,
35                                   const char value)
36 {
37    Eldbus_Message_Iter *var, *entry;
38 
39    eldbus_message_iter_arguments_append(array, "{sv}", &entry);
40    eldbus_message_iter_basic_append(entry, 's', key);
41 
42    var = eldbus_message_iter_container_new(entry, 'v', "y");
43    eldbus_message_iter_basic_append(var, 'y', value);
44    eldbus_message_iter_container_close(entry, var);
45    eldbus_message_iter_container_close(array, entry);
46 }
47 
48 static void
_elm_sys_notify_marshal_dict_string(Eldbus_Message_Iter * array,const char * key,const char * value)49 _elm_sys_notify_marshal_dict_string(Eldbus_Message_Iter *array,
50                                    const char *key,
51                                    const char *value)
52 {
53    Eldbus_Message_Iter *var, *entry;
54 
55    eldbus_message_iter_arguments_append(array, "{sv}", &entry);
56    eldbus_message_iter_basic_append(entry, 's', key);
57 
58    var = eldbus_message_iter_container_new(entry, 'v', "s");
59    eldbus_message_iter_basic_append(var, 's', value);
60    eldbus_message_iter_container_close(entry, var);
61    eldbus_message_iter_container_close(array, entry);
62 }
63 
64 static void
_get_capabilities_cb(void * data EINA_UNUSED,const Eldbus_Message * msg,Eldbus_Pending * pending EINA_UNUSED)65 _get_capabilities_cb(void *data EINA_UNUSED,
66                      const Eldbus_Message *msg,
67                      Eldbus_Pending *pending EINA_UNUSED)
68 {
69    char *val;
70    Eldbus_Message_Iter *arr;
71 
72    if (eldbus_message_error_get(msg, NULL, NULL) ||
73        !eldbus_message_arguments_get(msg, "as", &arr)) goto end;
74 
75    while (eldbus_message_iter_get_and_next(arr, 's', &val))
76      if (!strcmp(val, "body-markup"))
77        {
78           _has_markup = EINA_TRUE;
79           return;
80        }
81 
82 end:
83    _has_markup = EINA_FALSE;
84 }
85 
86 void
_elm_sys_notify_capabilities_get(void)87 _elm_sys_notify_capabilities_get(void)
88 {
89    EINA_SAFETY_ON_NULL_RETURN(_elm_sysnotif_proxy);
90 
91    if (!eldbus_proxy_call(_elm_sysnotif_proxy, "GetCapabilities",
92                          _get_capabilities_cb, NULL, -1, ""))
93      ERR("Error sending message: "INTERFACE".GetCapabilities.");
94 }
95 
96 static void
_close_notification_cb(void * data EINA_UNUSED,const Eldbus_Message * msg,Eldbus_Pending * pending EINA_UNUSED)97 _close_notification_cb(void *data EINA_UNUSED,
98                        const Eldbus_Message *msg,
99                        Eldbus_Pending *pending EINA_UNUSED)
100 {
101    const char *errname, *errmsg;
102 
103    if (eldbus_message_error_get(msg, &errname, &errmsg))
104      {
105         if (errmsg && errmsg[0] == '\0')
106           INF("Notification no longer exists.");
107         else
108           ERR("Eldbus Error: %s %s", errname, errmsg);
109      }
110 }
111 
112 EOLIAN static void
_elm_sys_notify_dbus_elm_sys_notify_interface_close(const Eo * obj EINA_UNUSED,void * sd EINA_UNUSED,unsigned int id)113 _elm_sys_notify_dbus_elm_sys_notify_interface_close(const Eo     *obj EINA_UNUSED,
114                                                     void         *sd  EINA_UNUSED,
115                                                     unsigned int  id)
116 {
117    EINA_SAFETY_ON_NULL_RETURN(_elm_sysnotif_proxy);
118 
119    if (!eldbus_proxy_call(_elm_sysnotif_proxy, "CloseNotification",
120                           _close_notification_cb, NULL, -1, "u", id))
121      ERR("Error sending message: "INTERFACE".CloseNotification.");
122 }
123 
124 static void
_notify_cb(void * data,const Eldbus_Message * msg,Eldbus_Pending * pending EINA_UNUSED)125 _notify_cb(void *data,
126            const Eldbus_Message *msg,
127            Eldbus_Pending *pending EINA_UNUSED)
128 {
129    const char *errname, *errmsg;
130    Elm_Sys_Notify_Send_Data *d = data;
131    unsigned int id = 0;
132 
133    if (eldbus_message_error_get(msg, &errname, &errmsg))
134      ERR("Error: %s %s", errname, errmsg);
135    else if (!eldbus_message_arguments_get(msg, "u", &id))
136      {
137         ERR("Error getting return values of "INTERFACE".Notify.");
138         id = 0;
139      }
140 
141    if (d->cb) d->cb((void *)d->data, id);
142    free(d);
143 }
144 
145 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)146 _elm_sys_notify_dbus_elm_sys_notify_interface_send(const Eo *obj EINA_UNUSED,
147                                                    void *sd EINA_UNUSED,
148                                                    unsigned int replaces_id,
149                                                    const char *icon,
150                                                    const char *summary,
151                                                    const char *body,
152                                                    Elm_Sys_Notify_Urgency urgency,
153                                                    int timeout,
154                                                    Elm_Sys_Notify_Send_Cb cb,
155                                                    const void *cb_data)
156 {
157    Eldbus_Message *msg;
158    Eldbus_Message_Iter *iter, *actions, *hints;
159    Elm_Sys_Notify_Send_Data *data;
160    char *body_free = NULL;
161    char *desk_free = NULL;
162    const char *deskentry = elm_app_desktop_entry_get();
163    const char *appname = elm_app_name_get();
164 
165    EINA_SAFETY_ON_NULL_RETURN(_elm_sysnotif_proxy);
166 
167    data = malloc(sizeof(Elm_Sys_Notify_Send_Data));
168    EINA_SAFETY_ON_NULL_GOTO(data, error);
169    data->cb = cb;
170    data->data = cb_data;
171 
172    if (!icon) icon = "";
173    if (!summary) summary = "";
174    if (!body)
175      body = "";
176    else if (!_has_markup)
177      body = body_free = elm_entry_markup_to_utf8(body);
178 
179    msg = eldbus_proxy_method_call_new(_elm_sysnotif_proxy, "Notify");
180 
181    iter = eldbus_message_iter_get(msg);
182    eldbus_message_iter_arguments_append(iter, "susssas", appname, replaces_id,
183                                         icon, summary, body, &actions);
184    /* actions */
185    eldbus_message_iter_container_close(iter, actions);
186 
187    /* hints */
188    eldbus_message_iter_arguments_append(iter, "a{sv}", &hints);
189    _elm_sys_notify_marshal_dict_byte(hints, "urgency", (char) urgency);
190 
191    if (strcmp(deskentry, ""))
192      {
193         deskentry = ecore_file_file_get(deskentry);
194         deskentry = desk_free = ecore_file_strip_ext(deskentry);
195         _elm_sys_notify_marshal_dict_string(hints, "desktop_entry", deskentry);
196      }
197    eldbus_message_iter_container_close(iter, hints);
198 
199    /* timeout */
200    eldbus_message_iter_arguments_append(iter, "i", timeout);
201 
202    eldbus_proxy_send(_elm_sysnotif_proxy, msg, _notify_cb, data, -1);
203    free(desk_free);
204    free(body_free);
205    return;
206 
207 error:
208    if (cb) cb((void *)cb_data, 0);
209 }
210 
211 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)212 _elm_sys_notify_dbus_elm_sys_notify_interface_simple_send(const Eo   *obj,
213                                                           void       *sd,
214                                                           const char *icon,
215                                                           const char *summary,
216                                                           const char *body)
217 {
218    _elm_sys_notify_dbus_elm_sys_notify_interface_send(obj, sd,
219                                                       0, icon, summary, body,
220                                                       ELM_SYS_NOTIFY_URGENCY_NORMAL,
221                                                       -1, NULL, NULL);
222 }
223 
224 static void
_on_notification_closed(void * data EINA_UNUSED,const Eldbus_Message * msg)225 _on_notification_closed(void *data EINA_UNUSED,
226                         const Eldbus_Message *msg)
227 {
228    const char *errname;
229    const char *errmsg;
230    Elm_Sys_Notify_Notification_Closed *d;
231 
232    if (eldbus_message_error_get(msg, &errname, &errmsg))
233      {
234         ERR("Eldbus Error: %s %s", errname, errmsg);
235         return;
236      }
237 
238    d = malloc(sizeof(*d));
239 
240    if (!eldbus_message_arguments_get(msg, "uu", &(d->id), &(d->reason)))
241      {
242         ERR("Error processing signal: "INTERFACE".NotificationClosed.");
243         goto cleanup;
244      }
245 
246    if (!ecore_event_add(ELM_EVENT_SYS_NOTIFY_NOTIFICATION_CLOSED, d,
247                         NULL, NULL)) goto cleanup;
248 
249    return;
250 
251 cleanup:
252    free(d);
253 }
254 
255 static void
_ev_action_invoked_free(void * data EINA_UNUSED,void * ev_data)256 _ev_action_invoked_free(void *data EINA_UNUSED,
257                         void *ev_data)
258 {
259    Elm_Sys_Notify_Action_Invoked *d = ev_data;
260 
261    free(d->action_key);
262    free(d);
263 }
264 
265 static void
_on_action_invoked(void * data EINA_UNUSED,const Eldbus_Message * msg)266 _on_action_invoked(void *data EINA_UNUSED,
267                    const Eldbus_Message *msg)
268 {
269    const char *errname;
270    const char *aux;
271 
272    Elm_Sys_Notify_Action_Invoked *d;
273 
274    if (eldbus_message_error_get(msg, &errname, &aux))
275      {
276         ERR("Eldbus Error: %s %s", errname, aux);
277         return;
278      }
279 
280    d = calloc(1, sizeof(*d));
281    if (!d)
282      {
283         ERR("Fail to allocate memory");
284         return;
285      }
286 
287    if (!eldbus_message_arguments_get(msg, "us", &(d->id), &aux))
288      {
289         ERR("Error processing signal: "INTERFACE".ActionInvoked.");
290         goto cleanup;
291      }
292 
293    d->action_key = strdup(aux);
294 
295    if (!ecore_event_add(ELM_EVENT_SYS_NOTIFY_ACTION_INVOKED, d,
296                         _ev_action_invoked_free, NULL)) goto cleanup;
297 
298    return;
299 
300 cleanup:
301    free(d->action_key);
302    free(d);
303 }
304 
305 static void
_release(void)306 _release(void)
307 {
308    if (_elm_sysnotif_proxy)
309      {
310         eldbus_proxy_unref(_elm_sysnotif_proxy);
311         _elm_sysnotif_proxy = NULL;
312      }
313 
314    if (_elm_sysnotif_obj)
315      {
316         eldbus_object_unref(_elm_sysnotif_obj);
317         _elm_sysnotif_obj = NULL;
318      }
319 }
320 
321 static void
_update(void)322 _update(void)
323 {
324    _release();
325    _elm_sysnotif_obj = eldbus_object_get(_elm_sysnotif_conn, BUS, OBJ);
326    _elm_sysnotif_proxy = eldbus_proxy_get(_elm_sysnotif_obj, INTERFACE);
327    _elm_sys_notify_capabilities_get();
328 
329    eldbus_proxy_signal_handler_add(_elm_sysnotif_proxy, "NotificationClosed",
330                                    _on_notification_closed, NULL);
331 
332    eldbus_proxy_signal_handler_add(_elm_sysnotif_proxy, "ActionInvoked",
333                                    _on_action_invoked, NULL);
334 }
335 
336 static void
_name_owner_get_cb(void * data EINA_UNUSED,const Eldbus_Message * msg,Eldbus_Pending * pending EINA_UNUSED)337 _name_owner_get_cb(void *data EINA_UNUSED,
338                    const Eldbus_Message *msg,
339                    Eldbus_Pending *pending EINA_UNUSED)
340 {
341    const char *errname, *errmsg;
342 
343    if (eldbus_message_error_get(msg, &errname, &errmsg))
344      ERR("Eldbus Error: %s %s", errname, errmsg);
345    else
346      _update();
347 }
348 
349 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)350 _name_owner_changed_cb(void *data EINA_UNUSED,
351                        const char *bus EINA_UNUSED,
352                        const char *old_id EINA_UNUSED,
353                        const char *new_id)
354 {
355    if ((!new_id) || (*new_id == '\0'))
356      _release();
357    else
358      _update();
359 }
360 
361 EOLIAN static Efl_Object *
_elm_sys_notify_dbus_efl_object_constructor(Eo * obj,void * sd EINA_UNUSED)362 _elm_sys_notify_dbus_efl_object_constructor(Eo   *obj,
363                                          void *sd  EINA_UNUSED)
364 {
365    /* Don't create the same object twice (singleton) */
366    if (!_elm_sysnotif_conn)
367      {
368         if (!elm_need_eldbus()) return NULL;
369 
370         _elm_sysnotif_conn = eldbus_connection_get(ELDBUS_CONNECTION_TYPE_SESSION);
371         if (!_elm_sysnotif_conn) return NULL;
372 
373         eldbus_name_owner_changed_callback_add(_elm_sysnotif_conn, BUS,
374                                                _name_owner_changed_cb, NULL,
375                                                EINA_FALSE);
376 
377         eldbus_name_owner_get(_elm_sysnotif_conn, BUS, _name_owner_get_cb, NULL);
378 
379         obj = efl_constructor(efl_super(obj, MY_CLASS));
380         return obj;
381      }
382 
383    ERR("Elm.Sys_Notify.Dbus is a singleton. It has already been created");
384    return NULL;
385 }
386 
387 EOLIAN static void
_elm_sys_notify_dbus_efl_object_destructor(Eo * obj,void * sd EINA_UNUSED)388 _elm_sys_notify_dbus_efl_object_destructor(Eo   *obj,
389                                         void *sd  EINA_UNUSED)
390 {
391    _release();
392 
393    eldbus_connection_unref(_elm_sysnotif_conn);
394    _elm_sysnotif_conn = NULL;
395    efl_destructor(efl_super(obj, MY_CLASS));
396 }
397 
398 
399 #include "elm_sys_notify_dbus_eo.c"
400 
401