1 module libnotify4d.notification;
2 import std..string,
3        std.traits;
4 import core.memory;
5 import gobject;
6 import glib.gvariant,
7        glib.gerror,
8        glib.gtypes,
9        glib.gmain;
10 import libnotify4d.notify;
11 
12 extern (C) {
13   struct NotifyNotificationPrivate;
14 
15   struct NotifyNotification {
16     GObject                    parent_object;
17     NotifyNotificationPrivate *priv;
18   }
19 
20   struct NotifyNotificationClass {
21     GObjectClass    parent_class;
22 
23     /* Signals */
24     void function (NotifyNotification*) closed;
25   }
26 
27 
28   /**
29   * NotifyUrgency:
30   * @NOTIFY_URGENCY_LOW: Low urgency. Used for unimportant notifications.
31   * @NOTIFY_URGENCY_NORMAL: Normal urgency. Used for most standard notifications.
32   * @NOTIFY_URGENCY_CRITICAL: Critical urgency. Used for very important notifications.
33   *
34   * The urgency level of the notification.
35   */
36   enum NotifyUrgency {
37     NOTIFY_URGENCY_LOW,
38     NOTIFY_URGENCY_NORMAL,
39     NOTIFY_URGENCY_CRITICAL,
40   }
41 
42   /**
43   * NotifyActionCallback:
44   * @notification:
45   * @action:
46   * @user_data:
47   *
48   * An action callback function.
49   */
50   alias NotifyActionCallback = void function (NotifyNotification *notification,
51                                               char               *action,
52                                               gpointer            user_data);
53 
54   /**
55   * NOTIFY_ACTION_CALLBACK:
56   * @func: The function to cast.
57   *
58   * A convenience macro for casting a function to a #NotifyActionCallback. This
59   * is much like G_CALLBACK().
60   */
61   NotifyActionCallback NOTIFY_ACTION_CALLBACK (F)(F func) if (isCallable!F && arity!func == 3) {
62     return cast(NotifyActionCallback)(func);
63   }
64 
65   NotifyNotification* notify_notification_new                  (const char         *summary,
66                                                                 const char         *_body,
67                                                                 const char         *icon);
68 
69   gboolean            notify_notification_update                (NotifyNotification *notification,
70                                                                  const char         *summary,
71                                                                  const char         *_body,
72                                                                  const char         *icon);
73 
74   gboolean            notify_notification_show                  (NotifyNotification *notification,
75                                                                  GError            **error);
76 
77   void                notify_notification_set_timeout           (NotifyNotification *notification,
78                                                                  gint                timeout);
79 
80   void                notify_notification_set_category          (NotifyNotification *notification,
81                                                                  const char         *category);
82 
83   void                notify_notification_set_urgency           (NotifyNotification *notification,
84                                                                  NotifyUrgency       urgency);
85 
86   void                notify_notification_set_hint              (NotifyNotification *notification,
87                                                                 const char         *key,
88                                                                 GVariant           *value);
89 
90   void                notify_notification_set_app_name          (NotifyNotification *notification,
91                                                                  const char         *app_name);
92 
93   void                notify_notification_clear_hints           (NotifyNotification *notification);
94 
95   void                notify_notification_add_action            (NotifyNotification *notification,
96                                                                  const char         *action,
97                                                                  const char         *label,
98                                                                  NotifyActionCallback callback,
99                                                                  gpointer            user_data,
100                                                                  GFreeFunc           free_func);
101 
102   void                notify_notification_clear_actions         (NotifyNotification *notification);
103   gboolean            notify_notification_close                 (NotifyNotification *notification,
104                                                                  GError            **error);
105 
106   gint                notify_notification_get_closed_reason     (const NotifyNotification *notification);
107 }
108 
109 class Notification {
110   NotifyNotification* notification;
111   string              summary,
112                       _body,
113                       icon;
114   GMainLoop*          loop;
115   int                 timeout;
116   bool                timeout_added;
117 
118   private {
119     static void*[size_t] frames;
120     static size_t[]      clear_ids;
121     static size_t        global_id;
122   }
123 
124   this () {
125     if (!Notify.isInitted) {
126       Notify.init("libnotify4d");
127     }
128 
129     this.notification = Notification.notificationNew(this.summary,
130                                                      this._body,
131                                                      this.icon);
132     this.loop = g_main_loop_new(null, FALSE);
133   }
134 
135   ~this() {
136     if (this.timeout_added && g_main_loop_is_running(this.loop)) {
137       g_main_loop_quit(this.loop);
138     }
139     g_main_loop_unref(this.loop);
140     Notification.clearFrames();
141   }
142 
143   this (string summary, string _body, string icon) {
144     this.summary = summary;
145     this._body   = _body;
146     this.icon    = icon;
147 
148     this();
149   }
150 
151   this (string summary, string _body) {
152     this.summary = summary;
153     this._body   = _body;
154 
155     this();
156   }
157 
158   private struct ActionFrame(CallBack, T) {
159     GMainLoop* loop;
160     CallBack   callback;
161     T*         payload;
162   }
163 
164   private static void clearFrames() {
165     foreach (id; Notification.clear_ids) {
166       GC.free(Notification.frames[id]);
167       this.frames.remove(id);
168     }
169 
170     Notification.clear_ids = [];
171   }
172 
173   private static void addFrames(AF: ActionFrame!(CallBack, T), CallBack: CallBack, T: T)(AF** afp) {
174     Notification.frames[Notification.global_id++] = cast(void*)*afp;
175   }
176 
177   static NotifyNotification* notificationNew(string summary, string _body, string icon) {
178     return notify_notification_new(summary.toStringz,
179                                    _body.toStringz,
180                                    icon.toStringz);
181   }
182 
183   void setSummary(string summary) {
184     this.summary = summary;
185   }
186 
187   void update(string summary, string _body, string icon) {
188     notify_notification_update(this.notification,
189                                summary.toStringz,
190                                _body.toStringz,
191                                icon.toStringz);
192   }
193 
194   void update() {
195     this.update(this.summary, this._body, this.icon);
196   }
197 
198   void show() {
199     GError* error;
200 
201     gboolean ret = notify_notification_show(this.notification, &error);
202 
203     if (!ret) {
204       string message = cast(string)error.message.fromStringz;
205 
206       new Error(message);
207     }
208 
209     if (this.timeout_added) {
210       g_main_loop_run(this.loop);
211     }
212   }
213 
214   void setTimeout(int timeout) {
215     this.timeout = timeout;
216     notify_notification_set_timeout(this.notification, timeout);
217   }
218 
219   void setCategory(string category) {
220     notify_notification_set_category(this.notification, category.toStringz);
221   }
222 
223   void setUrgency(NotifyUrgency urgency) {
224     notify_notification_set_urgency(this.notification, urgency);
225   }
226 
227   void setHint(string key, GVariant* value) {
228     notify_notification_set_hint(this.notification, key.toStringz, value);
229   }
230 
231   void setAppName(string app_name) {
232     notify_notification_set_app_name(this.notification, app_name.toStringz);
233   }
234   
235   void clearHints() {
236     notify_notification_clear_hints(this.notification);
237   }
238 
239   void addAction(CallBack: void function (NotifyNotification*, char*, T*), T: T)
240                 (string action, string label, CallBack callback, T* user_data = null, GFreeFunc free_func = null) {
241 
242     NotifyActionCallback cb = (NotifyNotification* notification, char* action, gpointer user_data) {
243       ActionFrame!(CallBack, T) af = *cast(ActionFrame!(CallBack, T)*)user_data;
244       GMainLoop* loop     = af.loop;
245       CallBack   callback = af.callback;
246       T*         payload  = af.payload;
247 
248       callback(notification, action, payload);
249 
250       Notification.clearFrames();
251 
252       g_main_loop_quit (loop);
253     };
254 
255     ActionFrame!(CallBack, T)* af = cast(ActionFrame!(CallBack, T)*)GC.malloc(ActionFrame!(CallBack, T).sizeof);
256     *af = ActionFrame!(CallBack, T)(loop, callback, user_data);
257     Notification.addFrames(&af);
258 
259     notify_notification_add_action (this.notification,
260                                     action.toStringz,
261                                     label.toStringz,
262                                     cb,
263                                     af,
264                                     free_func);
265 
266     if (!this.timeout_added) {
267       g_timeout_add(this.timeout + 4000, cast(GSourceFunc)(&g_main_loop_quit), this.loop);
268       this.timeout_added = true;
269     }
270   }
271 
272   void addAction(CallBack: void function (NotifyNotification*, char*, T*), T: T)
273                 (string label, CallBack callback, T* user_data = null, GFreeFunc free_func = null) {
274     this.addAction(label, label, callback, user_data, free_func);
275   }
276 
277   void clearActions() {
278     notify_notification_clear_actions(this.notification);
279   }
280 
281   void close() {
282     GError* error;
283 
284     if (this.timeout_added && g_main_loop_is_running(this.loop)) {
285       g_main_loop_quit(this.loop);
286     }
287 
288     auto ret = notify_notification_close(this.notification, &error);
289 
290     if (!ret) {
291       string message = cast(string)error.message.fromStringz;
292 
293       new Error(message);
294     }
295   }
296 
297   int getClosedReason() {
298     return notify_notification_get_closed_reason(this.notification);
299   }
300 }