1 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Notes ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3 ;; This is a binding to libnotify, wrapping some of the functionality.
6 ;; ‣ GTK specific stuff, such as:
7 ;; • The GtkWidget* attach argument in notify_notification_new
8 ;; • notify_notification_new_with_status_icon (takes a GtkStatusIcon*)
9 ;; • notify_notification_attach_to_widget (takes a GtkWidget*)
10 ;; • notify_notification_attach_to_status_icon (takes a GtkStatusIcon*)
11 ;; • notify_notification_set_geometry_hints (takes a GtkScreen*)
12 ;; • notify_notification_set_icon_from_pixbuf (takes a GdkPixbuf*)
15 ;; • notify_notification_add_action
16 ;; • notify_notification_clear_actions
17 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
19 (in-package :cl-notify
)
21 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Public Interface ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
23 (defclass notification
()
24 ((summary :initarg
:summary
)
26 (icon :initarg
:icon
:initform nil
)
27 (category :initform nil
)
28 (urgency :initform
:normal
)
30 (c-object :initform nil
)))
32 (defgeneric show-notification
(notification))
33 (defgeneric close-notification
(notification))
36 ;;;;;;;;;;;;;;;;;;;;;;; Generic class and library funcs. ;;;;;;;;;;;;;;;;;;;;;;;
39 "Initialize libnotify."
40 (if (= 0 (notify_init "cl-notify")) nil t
))
42 (defmethod initialize-instance :after
((n notification
)
43 &key summary body icon
)
44 (setf (slot-value n
'c-object
)
45 (notify-notification-new summary
46 (or body
(null-pointer))
47 (or icon
(null-pointer))
49 (when (null-pointer-p (slot-value n
'c-object
))
50 (error "Could not create notification object.")))
53 ;;;;;;;;;;;;;;;;;;;; Creating and destroying notifications ;;;;;;;;;;;;;;;;;;;;;
55 (defun destroy-notification (n)
56 "Unref the C object in the background. Consider using `with-notification'."
58 (unless (null-pointer-p (slot-value n
'c-object
))
59 (g-object-unref (slot-value n
'c-object
))))
61 (defmacro with-notification
(sym (summary &key body icon timeout urgency
)
63 "Evaluate the forms in FORMS with SYM bound to a notification object created
64 with a summary of SUMMARY and a body of BODY. If ICON, TIMEOUT and/or URGENCY
65 are set, use them too."
66 `(let ((,sym
(make-instance 'notification
74 `(setf (timeout ,sym
) ,timeout
))
76 `(setf (urgency ,sym
) ,urgency
))
80 (destroy-notification ,sym
))))
82 (defmacro dispatch-notification
(summary &key body icon timeout urgency
)
83 "Create and display a notification. For more complicated cases when you need
84 the symbol to stick around, use `with-notification'."
85 `(with-notification n
(,summary
86 ,@(when body
(list :body body
))
87 ,@(when icon
(list :icon icon
))
88 ,@(when timeout
(list :timeout timeout
))
89 ,@(when urgency
(list :urgency urgency
)))
90 (show-notification n
)))
92 (defmethod show-notification ((n notification
))
93 (unless (= 1 (notify-notification-show (slot-value n
'c-object
)
95 (error "Unknown error. TODO: Read the GError!"))
98 ;;;;;;;;;;;;;;;;;;;;; Modify attributes of a notification. ;;;;;;;;;;;;;;;;;;;;;
101 (defmethod (setf timeout
) (timeout (n notification
))
102 (notify-notification-set-timeout (slot-value n
'c-object
)
105 (defmethod update-notification ((n notification
)
106 &key summary body icon
108 "Update the notification N with a new summary, body and/or icon. If RE-SHOW,
109 then call show-notification afterwards to make the changes visible."
111 (when (= 0 (notify-notification-update (slot-value n
'c-object
)
112 (or summary
(slot-value n
'summary
))
113 (or body
(null-pointer))
114 (or icon
(null-pointer))))
115 (error "Couldn't update the notification."))
117 (show-notification n
)))
119 (defmethod (setf summary
) (summary (n notification
)
120 &optional
(re-show t
))
121 (update-notification n
:summary summary
:re-show re-show
))
123 (defmethod (setf body
) (body (n notification
)
124 &optional
(re-show t
))
125 (update-notification n
:body body
:re-show re-show
))
127 (defmethod (setf icon
) (icon (n notification
)
128 &optional
(re-show t
))
129 (update-notification n
:icon icon
:re-show re-show
))
131 (defmethod (setf urgency
) (urgency (n notification
))
132 (notify-notification-set-urgency (slot-value n
'c-object
)
135 ;;;;;;;;;;;;;;;;;;;; General server/connection information. ;;;;;;;;;;;;;;;;;;;;
137 (defmacro with-foreign-string-pointers
(syms freeing-func
&body body
)
138 "Execute BODY with the symbols in the list SYMS bound to foreign pointers,
139 each of which has been set to null. Once BODY has finished, release any
140 strings that aren't null with FREEING-FUNC. Evaluate to (progn BODY)."
142 `(with-foreign-objects
145 collecting
`(,s
:pointer
))
149 collecting
`(setf (mem-ref ,s
:pointer
) (null-pointer)))
156 collecting
`(unless (null-pointer-p (mem-ref ,s
:pointer
))
157 (funcall ,freeing-func
158 (mem-ref ,s
:pointer
)))))))
160 (defun server-info ()
161 (with-foreign-string-pointers
162 (name vendor version spec-version
)
165 (notify-get-server-info name vendor version spec-version
)
167 (mapcar (lambda (pptr)
168 (foreign-string-to-lisp (mem-ref pptr
:pointer
)))
169 (list name vendor version spec-version
))))