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	NotificationManager::Manager().RegisterService(*this);
160}
161
162
163DefaultNotificationService::~DefaultNotificationService()
164{
165	NotificationManager::Manager().UnregisterService(*this);
166	recursive_lock_destroy(&fLock);
167}
168
169
170/*!	\brief Notifies all registered listeners.
171	\param event The message defining the event
172	\param eventMask Only listeners with an event mask sharing at least one
173		common bit with this mask will receive the event.
174*/
175void
176DefaultNotificationService::NotifyLocked(const KMessage& event, uint32 eventMask)
177{
178	// Note: The following iterations support that the listener removes itself
179	// in the hook method. That's a property of the DoublyLinkedList iterator.
180
181	// notify all listeners about the event
182	DefaultListenerList::Iterator iterator = fListeners.GetIterator();
183	while (default_listener* listener = iterator.Next()) {
184		if ((eventMask & listener->eventMask) != 0)
185			listener->listener->EventOccurred(*this, &event);
186	}
187
188	// notify all listeners that all listeners have been notified
189	iterator = fListeners.GetIterator();
190	while (default_listener* listener = iterator.Next()) {
191		if ((eventMask & listener->eventMask) != 0)
192			listener->listener->AllListenersNotified(*this);
193	}
194}
195
196
197status_t
198DefaultNotificationService::AddListener(const KMessage* eventSpecifier,
199	NotificationListener& notificationListener)
200{
201	if (eventSpecifier == NULL)
202		return B_BAD_VALUE;
203
204	uint32 eventMask;
205	status_t status = ToEventMask(*eventSpecifier, eventMask);
206	if (status != B_OK)
207		return status;
208
209	default_listener* listener = new(std::nothrow) default_listener;
210	if (listener == NULL)
211		return B_NO_MEMORY;
212
213	listener->eventMask = eventMask;
214	listener->team = -1;
215	listener->listener = &notificationListener;
216
217	RecursiveLocker _(fLock);
218	if (fListeners.IsEmpty())
219		FirstAdded();
220	fListeners.Add(listener);
221
222	return B_OK;
223}
224
225
226status_t
227DefaultNotificationService::UpdateListener(const KMessage* eventSpecifier,
228	NotificationListener& notificationListener)
229{
230	return B_NOT_SUPPORTED;
231}
232
233
234status_t
235DefaultNotificationService::RemoveListener(const KMessage* eventSpecifier,
236	NotificationListener& notificationListener)
237{
238	RecursiveLocker _(fLock);
239
240	DefaultListenerList::Iterator iterator = fListeners.GetIterator();
241	while (default_listener* listener = iterator.Next()) {
242		if (listener->listener == &notificationListener) {
243			iterator.Remove();
244			delete listener;
245
246			if (fListeners.IsEmpty())
247				LastRemoved();
248			return B_OK;
249		}
250	}
251
252	return B_ENTRY_NOT_FOUND;
253}
254
255
256status_t
257DefaultNotificationService::ToEventMask(const KMessage& eventSpecifier,
258	uint32& eventMask)
259{
260	return eventSpecifier.FindInt32("event mask", (int32*)&eventMask);
261}
262
263
264void
265DefaultNotificationService::FirstAdded()
266{
267}
268
269
270void
271DefaultNotificationService::LastRemoved()
272{
273}
274
275
276//	#pragma mark - DefaultUserNotificationService
277
278
279DefaultUserNotificationService::DefaultUserNotificationService(const char* name)
280	: DefaultNotificationService(name)
281{
282	NotificationManager::Manager().AddListener("teams", TEAM_REMOVED, *this);
283}
284
285
286DefaultUserNotificationService::~DefaultUserNotificationService()
287{
288	NotificationManager::Manager().RemoveListener("teams", NULL, *this);
289}
290
291
292status_t
293DefaultUserNotificationService::AddListener(const KMessage* eventSpecifier,
294	NotificationListener& listener)
295{
296	if (eventSpecifier == NULL)
297		return B_BAD_VALUE;
298
299	uint32 eventMask = eventSpecifier->GetInt32(kEventMaskString, 0);
300
301	return _AddListener(eventMask, listener);
302}
303
304
305status_t
306DefaultUserNotificationService::UpdateListener(const KMessage* eventSpecifier,
307	NotificationListener& notificationListener)
308{
309	if (eventSpecifier == NULL)
310		return B_BAD_VALUE;
311
312	uint32 eventMask = eventSpecifier->GetInt32(kEventMaskString, 0);
313	bool addEvents = eventSpecifier->GetBool("add events", false);
314
315	RecursiveLocker _(fLock);
316
317	DefaultListenerList::Iterator iterator = fListeners.GetIterator();
318	while (default_listener* listener = iterator.Next()) {
319		if (*listener->listener == notificationListener) {
320			if (addEvents)
321				listener->eventMask |= eventMask;
322			else
323				listener->eventMask = eventMask;
324			return B_OK;
325		}
326	}
327
328	return B_ENTRY_NOT_FOUND;
329}
330
331
332status_t
333DefaultUserNotificationService::RemoveListener(const KMessage* eventSpecifier,
334	NotificationListener& notificationListener)
335{
336	RecursiveLocker _(fLock);
337
338	DefaultListenerList::Iterator iterator = fListeners.GetIterator();
339	while (default_listener* listener = iterator.Next()) {
340		if (listener->listener == &notificationListener) {
341			iterator.Remove();
342			delete listener;
343			return B_OK;
344		}
345	}
346
347	return B_ENTRY_NOT_FOUND;
348}
349
350
351status_t
352DefaultUserNotificationService::RemoveUserListeners(port_id port, uint32 token)
353{
354	UserMessagingListener userListener(fSender, port, token);
355
356	RecursiveLocker _(fLock);
357
358	DefaultListenerList::Iterator iterator = fListeners.GetIterator();
359	while (default_listener* listener = iterator.Next()) {
360		if (*listener->listener == userListener) {
361			iterator.Remove();
362			delete listener;
363
364			if (fListeners.IsEmpty())
365				LastRemoved();
366			return B_OK;
367		}
368	}
369
370	return B_ENTRY_NOT_FOUND;
371}
372
373
374status_t
375DefaultUserNotificationService::UpdateUserListener(uint32 eventMask,
376	port_id port, uint32 token)
377{
378	UserMessagingListener userListener(fSender, port, token);
379
380	RecursiveLocker _(fLock);
381
382	DefaultListenerList::Iterator iterator = fListeners.GetIterator();
383	while (default_listener* listener = iterator.Next()) {
384		if (*listener->listener == userListener) {
385			listener->eventMask |= eventMask;
386			return B_OK;
387		}
388	}
389
390	UserMessagingListener* copiedListener
391		= new(std::nothrow) UserMessagingListener(userListener);
392	if (copiedListener == NULL)
393		return B_NO_MEMORY;
394
395	status_t status = _AddListener(eventMask, *copiedListener);
396	if (status != B_OK)
397		delete copiedListener;
398
399	return status;
400}
401
402
403void
404DefaultUserNotificationService::EventOccurred(NotificationService& service,
405	const KMessage* event)
406{
407	int32 eventCode = event->GetInt32("event", -1);
408	team_id team = event->GetInt32("team", -1);
409
410	if (eventCode == TEAM_REMOVED && team >= B_OK) {
411		// check if we have any listeners from that team, and remove them
412		RecursiveLocker _(fLock);
413
414		DefaultListenerList::Iterator iterator = fListeners.GetIterator();
415		while (default_listener* listener = iterator.Next()) {
416			if (listener->team == team) {
417				iterator.Remove();
418				delete listener;
419			}
420		}
421	}
422}
423
424
425void
426DefaultUserNotificationService::AllListenersNotified(
427	NotificationService& service)
428{
429}
430
431
432status_t
433DefaultUserNotificationService::_AddListener(uint32 eventMask,
434	NotificationListener& notificationListener)
435{
436	default_listener* listener = new(std::nothrow) default_listener;
437	if (listener == NULL)
438		return B_NO_MEMORY;
439
440	listener->eventMask = eventMask;
441	listener->team = team_get_current_team_id();
442	listener->listener = &notificationListener;
443
444	RecursiveLocker _(fLock);
445	if (fListeners.IsEmpty())
446		FirstAdded();
447	fListeners.Add(listener);
448
449	return B_OK;
450}
451
452
453//	#pragma mark - NotificationManager
454
455
456/*static*/ NotificationManager&
457NotificationManager::Manager()
458{
459	return sManager;
460}
461
462
463/*static*/ status_t
464NotificationManager::CreateManager()
465{
466	new(&sManager) NotificationManager;
467	return sManager._Init();
468}
469
470
471NotificationManager::NotificationManager()
472{
473}
474
475
476NotificationManager::~NotificationManager()
477{
478}
479
480
481status_t
482NotificationManager::_Init()
483{
484	mutex_init(&fLock, "notification manager");
485
486	return fServiceHash.Init();
487}
488
489
490NotificationService*
491NotificationManager::_ServiceFor(const char* name)
492{
493	return fServiceHash.Lookup(name);
494}
495
496
497status_t
498NotificationManager::RegisterService(NotificationService& service)
499{
500	MutexLocker _(fLock);
501
502	if (_ServiceFor(service.Name()))
503		return B_NAME_IN_USE;
504
505	status_t status = fServiceHash.Insert(&service);
506	if (status == B_OK)
507		service.AcquireReference();
508
509	return status;
510}
511
512
513void
514NotificationManager::UnregisterService(NotificationService& service)
515{
516	MutexLocker _(fLock);
517	fServiceHash.Remove(&service);
518	service.ReleaseReference();
519}
520
521
522status_t
523NotificationManager::AddListener(const char* serviceName,
524	uint32 eventMask, NotificationListener& listener)
525{
526	char buffer[96];
527	KMessage specifier;
528	specifier.SetTo(buffer, sizeof(buffer), 0);
529	specifier.AddInt32(kEventMaskString, eventMask);
530
531	return AddListener(serviceName, &specifier, listener);
532}
533
534
535status_t
536NotificationManager::AddListener(const char* serviceName,
537	const KMessage* eventSpecifier, NotificationListener& listener)
538{
539	MutexLocker locker(fLock);
540	NotificationService* service = _ServiceFor(serviceName);
541	if (service == NULL)
542		return B_NAME_NOT_FOUND;
543
544	BReference<NotificationService> reference(service);
545	locker.Unlock();
546
547	return service->AddListener(eventSpecifier, listener);
548}
549
550
551status_t
552NotificationManager::UpdateListener(const char* serviceName,
553	uint32 eventMask, NotificationListener& listener)
554{
555	char buffer[96];
556	KMessage specifier;
557	specifier.SetTo(buffer, sizeof(buffer), 0);
558	specifier.AddInt32(kEventMaskString, eventMask);
559
560	return UpdateListener(serviceName, &specifier, listener);
561}
562
563
564status_t
565NotificationManager::UpdateListener(const char* serviceName,
566	const KMessage* eventSpecifier, NotificationListener& listener)
567{
568	MutexLocker locker(fLock);
569	NotificationService* service = _ServiceFor(serviceName);
570	if (service == NULL)
571		return B_NAME_NOT_FOUND;
572
573	BReference<NotificationService> reference(service);
574	locker.Unlock();
575
576	return service->UpdateListener(eventSpecifier, listener);
577}
578
579
580status_t
581NotificationManager::RemoveListener(const char* serviceName,
582	const KMessage* eventSpecifier, NotificationListener& listener)
583{
584	MutexLocker locker(fLock);
585	NotificationService* service = _ServiceFor(serviceName);
586	if (service == NULL)
587		return B_NAME_NOT_FOUND;
588
589	BReference<NotificationService> reference(service);
590	locker.Unlock();
591
592	return service->RemoveListener(eventSpecifier, listener);
593}
594
595
596//	#pragma mark -
597
598
599extern "C" void
600notifications_init(void)
601{
602	status_t status = NotificationManager::CreateManager();
603	if (status < B_OK) {
604		panic("Creating the notification manager failed: %s\n",
605			strerror(status));
606	}
607}
608
609
610#endif	// _KERNEL_MODE
611