1/*
2 * Copyright 2007-2009, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Axel Dörfler, axeld@pinc-software.de
7 *		Ingo Weinhold, bonefish@cs.tu-berlin.de
8 */
9
10
11#include <Notifications.h>
12
13#include <new>
14
15#include <team.h>
16
17
18#ifdef _KERNEL_MODE
19
20static const char* kEventMaskString = "event mask";
21
22NotificationManager NotificationManager::sManager;
23
24#endif
25
26
27// #pragma mark - NotificationListener
28
29
30NotificationListener::~NotificationListener()
31{
32}
33
34
35void
36NotificationListener::EventOccurred(NotificationService& service,
37	const KMessage* event)
38{
39}
40
41
42void
43NotificationListener::AllListenersNotified(NotificationService& service)
44{
45}
46
47
48bool
49NotificationListener::operator==(const NotificationListener& other) const
50{
51	return &other == this;
52}
53
54
55// #pragma mark - UserMessagingMessageSender
56
57
58#ifdef _KERNEL_MODE
59
60
61UserMessagingMessageSender::UserMessagingMessageSender()
62	:
63	fMessage(NULL),
64	fTargetCount(0)
65{
66}
67
68
69void
70UserMessagingMessageSender::SendMessage(const KMessage* message, port_id port,
71	int32 token)
72{
73	if ((message != fMessage && fMessage != NULL)
74		|| fTargetCount == MAX_MESSAGING_TARGET_COUNT) {
75		FlushMessage();
76	}
77
78	fMessage = message;
79	fTargets[fTargetCount].port = port;
80	fTargets[fTargetCount].token = token;
81	fTargetCount++;
82}
83
84
85void
86UserMessagingMessageSender::FlushMessage()
87{
88	if (fMessage != NULL && fTargetCount > 0) {
89		send_message(fMessage->Buffer(), fMessage->ContentSize(),
90			fTargets, fTargetCount);
91	}
92
93	fMessage = NULL;
94	fTargetCount = 0;
95}
96
97
98// #pragma mark - UserMessagingListener
99
100
101UserMessagingListener::UserMessagingListener(UserMessagingMessageSender& sender,
102		port_id port, int32 token)
103	:
104	fSender(sender),
105	fPort(port),
106	fToken(token)
107{
108}
109
110
111UserMessagingListener::~UserMessagingListener()
112{
113}
114
115
116void
117UserMessagingListener::EventOccurred(NotificationService& service,
118	const KMessage* event)
119{
120	fSender.SendMessage(event, fPort, fToken);
121}
122
123
124void
125UserMessagingListener::AllListenersNotified(NotificationService& service)
126{
127	fSender.FlushMessage();
128}
129
130
131//	#pragma mark - NotificationService
132
133
134NotificationService::~NotificationService()
135{
136}
137
138
139//	#pragma mark - default_listener
140
141
142default_listener::~default_listener()
143{
144	// Only delete the listener if it's one of ours
145	if (dynamic_cast<UserMessagingListener*>(listener) != NULL) {
146		delete listener;
147	}
148}
149
150
151//	#pragma mark - DefaultNotificationService
152
153
154DefaultNotificationService::DefaultNotificationService(const char* name)
155	:
156	fName(name)
157{
158	recursive_lock_init(&fLock, name);
159}
160
161
162DefaultNotificationService::~DefaultNotificationService()
163{
164	recursive_lock_destroy(&fLock);
165}
166
167
168/*!	\brief Notifies all registered listeners.
169	\param event The message defining the event
170	\param eventMask Only listeners with an event mask sharing at least one
171		common bit with this mask will receive the event.
172*/
173void
174DefaultNotificationService::NotifyLocked(const KMessage& event, uint32 eventMask)
175{
176	// Note: The following iterations support that the listener removes itself
177	// in the hook method. That's a property of the DoublyLinkedList iterator.
178
179	// notify all listeners about the event
180	DefaultListenerList::Iterator iterator = fListeners.GetIterator();
181	while (default_listener* listener = iterator.Next()) {
182		if ((eventMask & listener->eventMask) != 0)
183			listener->listener->EventOccurred(*this, &event);
184	}
185
186	// notify all listeners that all listeners have been notified
187	iterator = fListeners.GetIterator();
188	while (default_listener* listener = iterator.Next()) {
189		if ((eventMask & listener->eventMask) != 0)
190			listener->listener->AllListenersNotified(*this);
191	}
192}
193
194
195status_t
196DefaultNotificationService::AddListener(const KMessage* eventSpecifier,
197	NotificationListener& notificationListener)
198{
199	if (eventSpecifier == NULL)
200		return B_BAD_VALUE;
201
202	uint32 eventMask;
203	status_t status = ToEventMask(*eventSpecifier, eventMask);
204	if (status != B_OK)
205		return status;
206
207	default_listener* listener = new(std::nothrow) default_listener;
208	if (listener == NULL)
209		return B_NO_MEMORY;
210
211	listener->eventMask = eventMask;
212	listener->team = -1;
213	listener->listener = &notificationListener;
214
215	RecursiveLocker _(fLock);
216	if (fListeners.IsEmpty())
217		FirstAdded();
218	fListeners.Add(listener);
219
220	return B_OK;
221}
222
223
224status_t
225DefaultNotificationService::UpdateListener(const KMessage* eventSpecifier,
226	NotificationListener& notificationListener)
227{
228	return B_NOT_SUPPORTED;
229}
230
231
232status_t
233DefaultNotificationService::RemoveListener(const KMessage* eventSpecifier,
234	NotificationListener& notificationListener)
235{
236	RecursiveLocker _(fLock);
237
238	DefaultListenerList::Iterator iterator = fListeners.GetIterator();
239	while (default_listener* listener = iterator.Next()) {
240		if (listener->listener == &notificationListener) {
241			iterator.Remove();
242			delete listener;
243
244			if (fListeners.IsEmpty())
245				LastRemoved();
246			return B_OK;
247		}
248	}
249
250	return B_ENTRY_NOT_FOUND;
251}
252
253
254status_t
255DefaultNotificationService::Register()
256{
257	return NotificationManager::Manager().RegisterService(*this);
258}
259
260
261void
262DefaultNotificationService::Unregister()
263{
264	NotificationManager::Manager().UnregisterService(*this);
265}
266
267
268status_t
269DefaultNotificationService::ToEventMask(const KMessage& eventSpecifier,
270	uint32& eventMask)
271{
272	return eventSpecifier.FindInt32("event mask", (int32*)&eventMask);
273}
274
275
276void
277DefaultNotificationService::FirstAdded()
278{
279}
280
281
282void
283DefaultNotificationService::LastRemoved()
284{
285}
286
287
288//	#pragma mark - DefaultUserNotificationService
289
290
291DefaultUserNotificationService::DefaultUserNotificationService(const char* name)
292	: DefaultNotificationService(name)
293{
294	NotificationManager::Manager().AddListener("teams", TEAM_REMOVED, *this);
295}
296
297
298DefaultUserNotificationService::~DefaultUserNotificationService()
299{
300	NotificationManager::Manager().RemoveListener("teams", NULL, *this);
301}
302
303
304status_t
305DefaultUserNotificationService::AddListener(const KMessage* eventSpecifier,
306	NotificationListener& listener)
307{
308	if (eventSpecifier == NULL)
309		return B_BAD_VALUE;
310
311	uint32 eventMask = eventSpecifier->GetInt32(kEventMaskString, 0);
312
313	return _AddListener(eventMask, listener);
314}
315
316
317status_t
318DefaultUserNotificationService::UpdateListener(const KMessage* eventSpecifier,
319	NotificationListener& notificationListener)
320{
321	if (eventSpecifier == NULL)
322		return B_BAD_VALUE;
323
324	uint32 eventMask = eventSpecifier->GetInt32(kEventMaskString, 0);
325	bool addEvents = eventSpecifier->GetBool("add events", false);
326
327	RecursiveLocker _(fLock);
328
329	DefaultListenerList::Iterator iterator = fListeners.GetIterator();
330	while (default_listener* listener = iterator.Next()) {
331		if (*listener->listener == notificationListener) {
332			if (addEvents)
333				listener->eventMask |= eventMask;
334			else
335				listener->eventMask = eventMask;
336			return B_OK;
337		}
338	}
339
340	return B_ENTRY_NOT_FOUND;
341}
342
343
344status_t
345DefaultUserNotificationService::RemoveListener(const KMessage* eventSpecifier,
346	NotificationListener& notificationListener)
347{
348	RecursiveLocker _(fLock);
349
350	DefaultListenerList::Iterator iterator = fListeners.GetIterator();
351	while (default_listener* listener = iterator.Next()) {
352		if (listener->listener == &notificationListener) {
353			iterator.Remove();
354			delete listener;
355			return B_OK;
356		}
357	}
358
359	return B_ENTRY_NOT_FOUND;
360}
361
362
363status_t
364DefaultUserNotificationService::RemoveUserListeners(port_id port, uint32 token)
365{
366	UserMessagingListener userListener(fSender, port, token);
367
368	RecursiveLocker _(fLock);
369
370	DefaultListenerList::Iterator iterator = fListeners.GetIterator();
371	while (default_listener* listener = iterator.Next()) {
372		if (*listener->listener == userListener) {
373			iterator.Remove();
374			delete listener;
375
376			if (fListeners.IsEmpty())
377				LastRemoved();
378			return B_OK;
379		}
380	}
381
382	return B_ENTRY_NOT_FOUND;
383}
384
385
386status_t
387DefaultUserNotificationService::UpdateUserListener(uint32 eventMask,
388	port_id port, uint32 token)
389{
390	UserMessagingListener userListener(fSender, port, token);
391
392	RecursiveLocker _(fLock);
393
394	DefaultListenerList::Iterator iterator = fListeners.GetIterator();
395	while (default_listener* listener = iterator.Next()) {
396		if (*listener->listener == userListener) {
397			listener->eventMask |= eventMask;
398			return B_OK;
399		}
400	}
401
402	UserMessagingListener* copiedListener
403		= new(std::nothrow) UserMessagingListener(userListener);
404	if (copiedListener == NULL)
405		return B_NO_MEMORY;
406
407	status_t status = _AddListener(eventMask, *copiedListener);
408	if (status != B_OK)
409		delete copiedListener;
410
411	return status;
412}
413
414
415void
416DefaultUserNotificationService::EventOccurred(NotificationService& service,
417	const KMessage* event)
418{
419	int32 eventCode = event->GetInt32("event", -1);
420	team_id team = event->GetInt32("team", -1);
421
422	if (eventCode == TEAM_REMOVED && team >= B_OK) {
423		// check if we have any listeners from that team, and remove them
424		RecursiveLocker _(fLock);
425
426		DefaultListenerList::Iterator iterator = fListeners.GetIterator();
427		while (default_listener* listener = iterator.Next()) {
428			if (listener->team == team) {
429				iterator.Remove();
430				delete listener;
431			}
432		}
433	}
434}
435
436
437void
438DefaultUserNotificationService::AllListenersNotified(
439	NotificationService& service)
440{
441}
442
443
444status_t
445DefaultUserNotificationService::_AddListener(uint32 eventMask,
446	NotificationListener& notificationListener)
447{
448	default_listener* listener = new(std::nothrow) default_listener;
449	if (listener == NULL)
450		return B_NO_MEMORY;
451
452	listener->eventMask = eventMask;
453	listener->team = team_get_current_team_id();
454	listener->listener = &notificationListener;
455
456	RecursiveLocker _(fLock);
457	if (fListeners.IsEmpty())
458		FirstAdded();
459	fListeners.Add(listener);
460
461	return B_OK;
462}
463
464
465//	#pragma mark - NotificationManager
466
467
468/*static*/ NotificationManager&
469NotificationManager::Manager()
470{
471	return sManager;
472}
473
474
475/*static*/ status_t
476NotificationManager::CreateManager()
477{
478	new(&sManager) NotificationManager;
479	return sManager._Init();
480}
481
482
483NotificationManager::NotificationManager()
484{
485}
486
487
488NotificationManager::~NotificationManager()
489{
490}
491
492
493status_t
494NotificationManager::_Init()
495{
496	mutex_init(&fLock, "notification manager");
497
498	return fServiceHash.Init();
499}
500
501
502NotificationService*
503NotificationManager::_ServiceFor(const char* name)
504{
505	return fServiceHash.Lookup(name);
506}
507
508
509status_t
510NotificationManager::RegisterService(NotificationService& service)
511{
512	MutexLocker _(fLock);
513
514	if (_ServiceFor(service.Name()))
515		return B_NAME_IN_USE;
516
517	status_t status = fServiceHash.Insert(&service);
518	if (status == B_OK)
519		service.AcquireReference();
520
521	return status;
522}
523
524
525void
526NotificationManager::UnregisterService(NotificationService& service)
527{
528	MutexLocker _(fLock);
529	fServiceHash.Remove(&service);
530	service.ReleaseReference();
531}
532
533
534status_t
535NotificationManager::AddListener(const char* serviceName,
536	uint32 eventMask, NotificationListener& listener)
537{
538	char buffer[96];
539	KMessage specifier;
540	specifier.SetTo(buffer, sizeof(buffer), 0);
541	specifier.AddInt32(kEventMaskString, eventMask);
542
543	return AddListener(serviceName, &specifier, listener);
544}
545
546
547status_t
548NotificationManager::AddListener(const char* serviceName,
549	const KMessage* eventSpecifier, NotificationListener& listener)
550{
551	MutexLocker locker(fLock);
552	NotificationService* service = _ServiceFor(serviceName);
553	if (service == NULL)
554		return B_NAME_NOT_FOUND;
555
556	BReference<NotificationService> reference(service);
557	locker.Unlock();
558
559	return service->AddListener(eventSpecifier, listener);
560}
561
562
563status_t
564NotificationManager::UpdateListener(const char* serviceName,
565	uint32 eventMask, NotificationListener& listener)
566{
567	char buffer[96];
568	KMessage specifier;
569	specifier.SetTo(buffer, sizeof(buffer), 0);
570	specifier.AddInt32(kEventMaskString, eventMask);
571
572	return UpdateListener(serviceName, &specifier, listener);
573}
574
575
576status_t
577NotificationManager::UpdateListener(const char* serviceName,
578	const KMessage* eventSpecifier, NotificationListener& listener)
579{
580	MutexLocker locker(fLock);
581	NotificationService* service = _ServiceFor(serviceName);
582	if (service == NULL)
583		return B_NAME_NOT_FOUND;
584
585	BReference<NotificationService> reference(service);
586	locker.Unlock();
587
588	return service->UpdateListener(eventSpecifier, listener);
589}
590
591
592status_t
593NotificationManager::RemoveListener(const char* serviceName,
594	const KMessage* eventSpecifier, NotificationListener& listener)
595{
596	MutexLocker locker(fLock);
597	NotificationService* service = _ServiceFor(serviceName);
598	if (service == NULL)
599		return B_NAME_NOT_FOUND;
600
601	BReference<NotificationService> reference(service);
602	locker.Unlock();
603
604	return service->RemoveListener(eventSpecifier, listener);
605}
606
607
608//	#pragma mark -
609
610
611extern "C" void
612notifications_init(void)
613{
614	status_t status = NotificationManager::CreateManager();
615	if (status < B_OK) {
616		panic("Creating the notification manager failed: %s\n",
617			strerror(status));
618	}
619}
620
621
622#endif	// _KERNEL_MODE
623