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 }