1// Package notify provides an implementation of the Gnome DBus notifications
2// specification.
3package notify
4
5import "github.com/godbus/dbus"
6
7// Notification object paths and interfaces.
8const (
9	DbusObjectPath               = "/org/freedesktop/Notifications"
10	DbusInterfacePath            = "org.freedesktop.Notifications"
11	SignalNotificationClosed     = "org.freedesktop.Notifications.NotificationClosed"
12	SignalActionInvoked          = "org.freedesktop.Notifications.ActionInvoked"
13	CallGetCapabilities          = "org.freedesktop.Notifications.GetCapabilities"
14	CallCloseNotification        = "org.freedesktop.Notifications.CloseNotification"
15	CallNotify                   = "org.freedesktop.Notifications.Notify"
16	CallGetServerInformation     = "org.freedesktop.Notifications.GetServerInformation"
17	DbusMemberActionInvoked      = "ActionInvoked"
18	DbusMemberNotificationClosed = "NotificationClosed"
19)
20
21// Notification expire timeout.
22const (
23	ExpiresDefault = -1
24	ExpiresNever   = 0
25)
26
27// Notification Categories
28const (
29	ClassDevice              = "device"
30	ClassDeviceAdded         = "device.added"
31	ClassDeviceError         = "device.error"
32	ClassDeviceRemoved       = "device.removed"
33	ClassEmail               = "email"
34	ClassEmailArrived        = "email.arrived"
35	ClassEmailBounced        = "email.bounced"
36	ClassIm                  = "im"
37	ClassImError             = "im.error"
38	ClassImReceived          = "im.received"
39	ClassNetwork             = "network"
40	ClassNetworkConnected    = "network.connected"
41	ClassNetworkDisconnected = "network.disconnected"
42	ClassNetworkError        = "network.error"
43	ClassPresence            = "presence"
44	ClassPresenceOffline     = "presence.offline"
45	ClassPresenceOnline      = "presence.online"
46	ClassTransfer            = "transfer"
47	ClassTransferComplete    = "transfer.complete"
48	ClassTransferError       = "transfer.error"
49)
50
51// Urgency Levels
52const (
53	UrgencyLow      = byte(0)
54	UrgencyNormal   = byte(1)
55	UrgencyCritical = byte(2)
56)
57
58// Hints
59const (
60	HintActionIcons   = "action-icons"
61	HintCategory      = "category"
62	HintDesktopEntry  = "desktop-entry"
63	HintImageData     = "image-data"
64	HintImagePath     = "image-path"
65	HintResident      = "resident"
66	HintSoundFile     = "sound-file"
67	HintSoundName     = "sound-name"
68	HintSuppressSound = "suppress-sound"
69	HintTransient     = "transient"
70	HintX             = "x"
71	HintY             = "y"
72	HintUrgency       = "urgency"
73)
74
75// Capabilities is a struct containing the capabilities of the notification
76// server.
77type Capabilities struct {
78	// Supports using icons instead of text for displaying actions.
79	ActionIcons bool
80
81	// The server will provide any specified actions to the user.
82	Actions bool
83
84	// Supports body text. Some implementations may only show the summary.
85	Body bool
86
87	// The server supports hyperlinks in the notifications.
88	BodyHyperlinks bool
89
90	// The server supports images in the notifications.
91	BodyImages bool
92
93	// Supports markup in the body text.
94	BodyMarkup bool
95
96	// The server will render an animation of all the frames in a given
97	// image array.
98	IconMulti bool
99
100	// Supports display of exactly 1 frame of any given image array.
101	IconStatic bool
102
103	// The server supports persistence of notifications. Notifications will
104	// be retained until they are acknowledged or removed by the user or
105	// recalled by the sender.
106	Persistence bool
107
108	// The server supports sounds on notifications.
109	Sound bool
110}
111
112// GetCapabilities returns the capabilities of the notification server.
113func GetCapabilities() (c Capabilities, err error) {
114	conn, err := dbus.SessionBus()
115	if err != nil {
116		return
117	}
118
119	obj := conn.Object(DbusInterfacePath, DbusObjectPath)
120	call := obj.Call(CallGetCapabilities, 0)
121	if err = call.Err; err != nil {
122		return
123	}
124
125	s := []string{}
126	if err = call.Store(&s); err != nil {
127		return
128	}
129
130	for _, v := range s {
131		switch v {
132		case "action-icons":
133			c.ActionIcons = true
134			break
135		case "actions":
136			c.Actions = true
137			break
138		case "body":
139			c.Body = true
140			break
141		case "body-hyperlinks":
142			c.BodyHyperlinks = true
143			break
144		case "body-images":
145			c.BodyImages = true
146			break
147		case "body-markup":
148			c.BodyMarkup = true
149			break
150		case "icon-multi":
151			c.IconMulti = true
152			break
153		case "icon-static":
154			c.IconStatic = true
155			break
156		case "persistence":
157			c.Persistence = true
158			break
159		case "sound":
160			c.Sound = true
161			break
162		}
163	}
164	return
165}
166
167// ServerInformation is a struct containing information about the server such
168// as its name and version.
169type ServerInformation struct {
170	// The name of the notification server daemon
171	Name string
172
173	// The vendor of the notification server
174	Vendor string
175
176	// Version of the notification server
177	Version string
178
179	// Spec version the notification server conforms to
180	SpecVersion string
181}
182
183// GetServerInformation returns information about the notification server such
184// as its name and version.
185func GetServerInformation() (i ServerInformation, err error) {
186	conn, err := dbus.SessionBus()
187	if err != nil {
188		return
189	}
190
191	obj := conn.Object(DbusInterfacePath, DbusObjectPath)
192	call := obj.Call(CallGetServerInformation, 0)
193	if err = call.Err; err != nil {
194		return
195	}
196
197	err = call.Store(&i.Name, &i.Vendor, &i.Version, &i.SpecVersion)
198	return
199}
200
201// Notification is a struct which describes the notification to be displayed
202// by the notification server.
203type Notification struct {
204	// The optional name of the application sending the notification.
205	// Can be blank.
206	AppName string
207
208	// The optional notification ID that this notification replaces.
209	ReplacesID uint32
210
211	// The optional program icon of the calling application.
212	AppIcon string
213
214	// The summary text briefly describing the notification.
215	Summary string
216
217	// The optional detailed body text.
218	Body string
219
220	// The actions send a request message back to the notification client
221	// when invoked.
222	Actions []string
223
224	// Hints are a way to provide extra data to a notification server.
225	Hints map[string]interface{}
226
227	// The timeout time in milliseconds since the display of the
228	// notification at which the notification should automatically close.
229	Timeout int32
230}
231
232// NewNotification creates a new notification object with some basic
233// information.
234func NewNotification(summary, body string) Notification {
235	return Notification{
236		Summary: summary,
237		Body:    body,
238		Timeout: ExpiresDefault,
239	}
240}
241
242// Show sends the information in the notification object to the server to be
243// displayed.
244func (n Notification) Show() (id uint32, err error) {
245	conn, err := dbus.SessionBus()
246	if err != nil {
247		return
248	}
249
250	// We need to convert the interface type of the map to dbus.Variant as
251	// people dont want to have to import the dbus package just to make use
252	// of the notification hints.
253	hints := map[string]dbus.Variant{}
254	for k, v := range n.Hints {
255		hints[k] = dbus.MakeVariant(v)
256	}
257
258	obj := conn.Object(DbusInterfacePath, DbusObjectPath)
259	call := obj.Call(
260		CallNotify,
261		0,
262		n.AppName,
263		n.ReplacesID,
264		n.AppIcon,
265		n.Summary,
266		n.Body,
267		n.Actions,
268		hints,
269		n.Timeout)
270	if err = call.Err; err != nil {
271		return
272	}
273
274	err = call.Store(&id)
275	return
276}
277
278// CloseNotification closes the notification if it exists using its id.
279func CloseNotification(id uint32) (err error) {
280	conn, err := dbus.SessionBus()
281	if err != nil {
282		return
283	}
284
285	obj := conn.Object(DbusInterfacePath, DbusObjectPath)
286	call := obj.Call(CallCloseNotification, 0, id)
287	err = call.Err
288	return
289}
290